From ea1e8a9266b650b7761483baada24a143877641e Mon Sep 17 00:00:00 2001 From: Kirubakaran Date: Sun, 24 Aug 2025 22:09:18 +0530 Subject: [PATCH] feat(backend): implemented the backend of station dashboard web app --- backend/.env | 15 + backend/__pycache__/models.cpython-313.pyc | Bin 0 -> 2980 bytes backend/core/__init__.py | 0 .../core/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 184 bytes .../influxdb_client.cpython-313.pyc | Bin 0 -> 4387 bytes .../__pycache__/mqtt_client.cpython-313.pyc | Bin 0 -> 5152 bytes .../protobuf_decoder.cpython-313.pyc | Bin 0 -> 6601 bytes backend/core/mqtt_client.py | 75 +++ backend/core/protobuf_decoder.py | 157 +++++++ backend/main.py | 436 ++++++++++++++++++ backend/models.py | 35 ++ backend/proto/__init__.py | 0 .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 185 bytes .../vec_payload_chgSt_pb2.cpython-313.pyc | Bin 0 -> 6107 bytes backend/proto/vec_payload_chgSt.proto | 162 +++++++ backend/proto/vec_payload_chgSt_pb2.py | 70 +++ backend/proto/vec_payload_chgSt_pb2.pyi | 287 ++++++++++++ backend/requirements.txt | 7 + 18 files changed, 1244 insertions(+) create mode 100644 backend/.env create mode 100644 backend/__pycache__/models.cpython-313.pyc create mode 100644 backend/core/__init__.py create mode 100644 backend/core/__pycache__/__init__.cpython-313.pyc create mode 100644 backend/core/__pycache__/influxdb_client.cpython-313.pyc create mode 100644 backend/core/__pycache__/mqtt_client.cpython-313.pyc create mode 100644 backend/core/__pycache__/protobuf_decoder.cpython-313.pyc create mode 100644 backend/core/mqtt_client.py create mode 100644 backend/core/protobuf_decoder.py create mode 100644 backend/main.py create mode 100644 backend/models.py create mode 100644 backend/proto/__init__.py create mode 100644 backend/proto/__pycache__/__init__.cpython-313.pyc create mode 100644 backend/proto/__pycache__/vec_payload_chgSt_pb2.cpython-313.pyc create mode 100644 backend/proto/vec_payload_chgSt.proto create mode 100644 backend/proto/vec_payload_chgSt_pb2.py create mode 100644 backend/proto/vec_payload_chgSt_pb2.pyi create mode 100644 backend/requirements.txt diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..bc44926 --- /dev/null +++ b/backend/.env @@ -0,0 +1,15 @@ +# --- Flask Application Settings --- +# This is a secret key used by Flask for session management. +# You can generate a new one with: python -c 'import os; print(os.urandom(24).hex())' +SECRET_KEY="80473e17c5707e19252ef3736fba32805be21a9b3e914190" + +# --- PostgreSQL Database Connection --- +# Replace with your actual database credentials. +# Format: postgresql://:@:/ +DATABASE_URL="postgresql://swap_app_user:2004@localhost:5432/swap_station_db" + +# --- MQTT Broker Connection --- +MQTT_BROKER="mqtt-dev.upgrid.in" +MQTT_PORT="1883" +MQTT_USER="guest" +MQTT_PASSWORD="password" diff --git a/backend/__pycache__/models.cpython-313.pyc b/backend/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc15cfc97de96fc228f3c9b3692d1fbdd438dec4 GIT binary patch literal 2980 zcmb_eO>7fK6yCMh|37j5C9L>d80Sr04d{+DS-a3#IKye)Hzd`{w8Q zee-tH<8d<3gyY{Qzp*mR?{u);EL~-@0m^fRXLxp&nPD*-pt@<+G-Jl*025)ynW?TT z=8iJs3~xTh@LYsjFxKu)b-O8ZN5NLZ(*mB>_juY2PaAmJ-{WadID*bL-5|&kCx7qG zRXLVK=^TtKGyIqH*J9mM7#-Z#Xq!Jn_ne6^nB^I4;#q9wO_+;V7Cc>#cRH|o-x+pQ z-pq3$6Snuo4EMlTrr3@Z&{p0SGGphV@b(?Nm3P4IF5ZdV30KffxceHyZ5yoqczgk6 zFw&5sYXLEk1$#hJ0(ue!;-W4_MGZ|XuCv6I!BSepIU$8|?YjWHxo#rXtRgLE5wR;- zSr(%*YCnRRbr#dOtTXdL53vh^BBqfb5T_ud)p%B>x?2#Iv!dLc84!de32VA6DM(Ra z+9n8bH3o_)9wpZ6s+>(L#1>X`lt7r6q&TtiI+m0KvC{+7Wr!yuYT82;$AzS*CCR8D zXu7CNF+tS9HJa6tCJ5LE2it@nL+)O`7@<+nBC`_CM#ac9(o(vbiF}1(X*H%Q5&of= z;prNC^{-L%Y9_)C+Oi{*=r z^DF+A<81!g#=u}PSIAX|R{b^q+GPDiL;n%2qZAxRzo z8gGN$orgm2rnm_R0x5ndv}5P&76a|>i|!Sz4#v|L^S)@&^LqaidH@LRGYD<#CTg29 zZxP!;V)_i-#Pr|C10dQzQSmn&QWney2QfV%9-@LiKX`T_yp;~B-uU~1NdNo?|w+3(JXEt1(VyF-*EmR(qA5^tx-!|ea zp_c1hetIwG&eg}7rzqn-%in%$Wrjy9!E&&AZ8cmAuf^)~&HJTbYv}X*?1pcs;wU?+ zPT5#=peWH1u&Rl?*1MJl)doOLXm)^2BdrJq{`@feN`e*NJ zP*Uh0zppLO6xi3ci#TtA%ysuIMaTESV8~l{m}Y}wW`|e8dno)aQ*FExb{{wt+I3)e zUmyJ7wkICq-FP_R33`cb4wCzhnrKrRJpN)&z0{xx$tW;)Z*eh@Mw%ujP#`1bWL1m@ zmQ)-_N}8@>DJIGRSxqFsd!oZm_~P&aExtooh@&MjE9)J;22DosBo;}Dqeu7@Y}sM& zB-J{^9qCEjQR>ixy~I8(>S$4d8p$nXl^8KA>OYZBrOtQH6?_R+2aYq^G>ErmmStZvAHQbK{$XXTz9*5qX^Z37 oD=U-53xx|?45(XfCwqA%T1*y_TMVdM#~kd*&iF3|RO34T0Q+NIX#fBK literal 0 HcmV?d00001 diff --git a/backend/core/__init__.py b/backend/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/__pycache__/__init__.cpython-313.pyc b/backend/core/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b8d7442c3553d36eb93d13f797758e96a02b7f7 GIT binary patch literal 184 zcmey&%ge<81kbfsWq|0%AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkqYkXRDad;?$zz z81KxY(xk)~m(=3ylKg_0u+-$-{N((+nBelng5Z+Gl1x-~cxsYkK|xGXVsdtBUP?@I veo<;ne0*kJW=VX!UP0w84x8Nkl+v73yCPPgJs@`!gBTx~85tRin1L(+!GAJ1 literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/influxdb_client.cpython-313.pyc b/backend/core/__pycache__/influxdb_client.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88926cf0a864a30cd91c046d267db12026a963fd GIT binary patch literal 4387 zcmaJ^U2qfE6}~I&O0q25@(;qu7AynVdfbbjD#tpo;yF%H#mBF&vPnS~*h^T_B_++KRLt5dM7#;6N1h09mUZC?@{c<2UOGC3MG)w=o zaTVlHqHIeTxZpFb-U-y3Xfuiv5wnbz?QByJxLyp6maSZ&1C^{)Q-Dg;t0^3H$XR_m zAZ0#?wPK_2G-^w{(WY>YLlmr0-ZS1@<~Vyw=VMWt-iqQaHph*Gc7znFN|uRHjcC84 zGGhzkTnZJX(r&9wQ7Lr9xdo-DuqU_g1YCtA$WrcQPuEq`_w7q!!6zQtHwfN2KQ%X1 z8a;Q@9gVa7*M?TSX!Ew;#eW?yhV~Ug`@ZbRhxX+|!@wGO;%m;gy#B~{c)4}sf1fn9 zuObI53&gfJ?!9v_k#9Nr$k+Fzwf(yl1{mLJaO3D_(H+q+x*zr)t#{tv-+0vJ{IiRJ zexllu-5_W_gokjzo5an5P+NAD2uZM4yaGK`#evd}m}9gIJeB!e*)2Jmut&*$HKnIC zR_J*7*{t_8;1SaHj_W-W4&2&~4eRnpNP0S``sn(^fQi0ii5d zER&A86VMLBQ;xUVk}-F@&FzC2Za*AVl(1YZut6ZNDWoY_{vS7Yuk-g6|nX{^8| z#e|v!aLEW)WK~!TGG1>Kr?p-$iEo+}xOgea?$UG|?q$ayz@O-6)szW$C4R&dhOj3k zs^x~?9vcHdB2+Ta2 zf_El{u0=a5YF4A6n2d}VRQI$0K{33Q!;l;Rv6dTcZ{U@*s`r?>%3ObvidLcAI5 z4Hu}X;$*IK4K`oGsxDe-4M}Hm!D=<&l!BJU;tV85?9QBHXC&7#fHBAsjY$er1?atO zxYeFv5ZGM|>@5WLE(P|@)_?6m&1})%_0ZpSdwR*g?_Ul6tK5yw zh1^oZZmYj>p=+suU13mNBgB%9EyduTLU7OG`K91%#o&=b@W|ufn^#ZH_C0NCTWVsz zY7H%jpK`_4aG^E4D1I@0XZrUu`8UrjMb0d@ZJHmN8@lcMEdFu)lb_s86}k>TZaebS zCO>Zb(eozM@fNkxLf7>y`~25Ou8tIaY{ADa(qGiyslV6u$M!$8|FtVmXlKkowfAoD zt}lQ1Z2sJ2{%j(j5c4gmzxmQuG^6TzOrh?()S{Me*DEJP1x8elGw;BP#FC9^8 zfJP5!@4yb{gT}^ztjgT1MxtL$8BfQYBscsaEi2$S ze7-sagC6A^@+Cb$A<$!;d=kP_Iip-J%*9;xeg~>@#Nm&=Z@ux0Yvew+LP@-w4phSS zb`*Bxu6U1G51K%-B5FF|Apt)~3k7^-&A$?BzGf}r9k?vaWMr|IeRrpL5iq77aC0Xa zX|k%HNJ6*gz4zEjz%78=Y9YQ#zzbQZQxh|Q1LU%B7%GD%TN^I%G9XM6ldROBWk~~) z0Dz(-4SGsb6@y7;XEIvYQ_2UEhBqT%P=p7Fz^{_k$dU#%ZBQ2tw<2C549x?y5?WZP zqqRXTNUTA)V=zbiEmQy!Uew$=%PhBTm_I&u{G$_B`)8y7j2yf%RS4`@YhCD<4|MPw`fZBpc!5Z5PW(R> C%Egla literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/mqtt_client.cpython-313.pyc b/backend/core/__pycache__/mqtt_client.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b82dd2d90ace5ca9e3d821c3b1ac66c551de129 GIT binary patch literal 5152 zcma(VTWl29_0G=h?(BN}z&vW~*dC05Y!cRh4UJ8J_(8$Y7})U=6FZrVXUF!I@y_PX zY$)3w)T$MzNJN!TtF%&xR0+rrAVtFb6zES}sZwVZTec>sMJhk(7ll@d(x0AtXJ%(i zOq+{2bLO0T&%JZrcc0eQCJ~HJmOq)fARzR2^5PH9L!KT1XKaO@zK-c@cs_$Hi0lB zAsmqykRQQZ7WFU^+l+q1gM6+T;g}Q!7?)xIc_|JsA@Kl{QUYL&l*B?7?PG9lCrZ^g z$v57!tU*1m6|67gutoXj$-_z^r)#EY&1j-(6bhPZ<&A;}RB=`_O=VgWi`Y;B%@?M{ zDFcg&NKjo9-#9TkDw>u;7NnD-GkH^lKVVz1TERK-_*rWPWU0Dhny-jw^45%4FvLUI zVKJu_wL(rSsCmsyCslG5e-M4M@c-$DppE^^Wi;+{&?uipZ$}Z@?NLND)}Z@3Lt~;& z*3mJxL-*~5#$MN9jYmJO$Ex_ddUfCNH9mE`(Z@3Dd@Oi&RQKM^xYR;iVzMX$Ui0jz z1FL7_b*LMxrq}q~@g^V3H2YZa>_8#@3jy87GHpHn06Wmy+!@UhfiwQgurz8lLs@>@Uf9yPRw2ABw-gX)G6-m z=+pu|mp6S@JvnGWnbqXt8QIjVRKnp*O`pO<|Cl&5CSH$g38*8W9)Oc574q+uG;+9v zEYsMRWitUR(HrNSw+8zrvLM}@IGV?$No8V4Gf!JaapI(=&Khul3F(Ydl-$%3#$AO^ z5WRrHCsYG#6GQ{B4@qk2;#o{~a@GckF74Za9M=3RfUnP?2Yl^?c!h6T;G35>yl}B* zd0o@RgbSL)i?z$mofqquH}6^C#ec=n`ljD;A0-yp$^n#VEJ zvGr>E$L;62OEnj27WlU1O($Dgt3VNLy|-b=l|S@%)vN3F|3!=ONb#Lb@D`)=)96#5^tsCnD@-ly<|NUdf$cp*5=sQ&=uGRkri(>I2Y zl0j(P%VLP+DwL__Qkdus$X^jZ*FmjgpEpb5y4gYuxYfMtOMuv`=GlOjjRjIer-)`_ zD|ejcLtG+%)iX-*EJreJAaj~eCCWX1JkY{*W=1Qxb&fiVU+$px8I#r0Zw>salTArFGApOyzn3+$WY>PaYiXnp7-H!)K*g1zVmAcfG%J$BwRYq-%4z zUMfwRD$Y+*S+Hj750t#_3icwex(BJYo^Y{T_lk%$tAwGR8aYiINpViJi1P)@jXhFU zoP_BMPsMR7P%tI)B$9KAr8vZ~>kiFxf2sddy?toRZalRhy!Ei<)wy+d z+wZj7uMW>Q+8cj({z%o4H&uj9S6*5Ww!_K~j$Ju&*7b`OHfu|ZZ{~GfBpxwaw1{D`=2zEZ9RkT4tLrk$t`_w`4QC-w!fvt)45*{*CNy5Hzj35&L8!ou zS34!>t5%gu`>JQ6y&W=Bf$Qg{S@$``Ord>jJDP$%yZq|6t*TU;Tj*H9BwGG`--@IW z_A)`a>mbGugP{i4?}_sehF-0uIe-R`_)Vk_0y9PnAvLNKr3@!tRL<�wwX_k5#Qm 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 literal 0 HcmV?d00001 diff --git a/backend/core/mqtt_client.py b/backend/core/mqtt_client.py new file mode 100644 index 0000000..b459470 --- /dev/null +++ b/backend/core/mqtt_client.py @@ -0,0 +1,75 @@ +import paho.mqtt.client as mqtt +import uuid +import time +import threading +import socket + +class MqttClient: + """ + Handles the connection and message processing for a single MQTT station. + This is a standard Python class, with no GUI dependencies. + """ + def __init__(self, broker, port, user, password, station_id, on_message_callback): + super().__init__() + self.broker = broker + self.port = port + self.user = user + self.password = password + self.station_id = station_id + self.on_message_callback = on_message_callback + + # Generate a unique client ID to prevent connection conflicts + unique_id = str(uuid.uuid4()) + self.client_id = f"WebApp-Backend-{self.station_id}-{unique_id}" + + self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, self.client_id) + + # Assign callback functions + self.client.on_connect = self.on_connect + self.client.on_message = self.on_message + self.client.on_disconnect = self.on_disconnect + + if self.user and self.password: + self.client.username_pw_set(self.user, self.password) + + def on_connect(self, client, userdata, flags, rc, properties): + """Callback for when the client connects to the broker.""" + if rc == 0: + print(f"Successfully connected to MQTT broker for station: {self.station_id}") + # Subscribe to all topics for this station using a wildcard + topic_base = f"VEC/batterySmartStation/v100/{self.station_id}/#" + self.client.subscribe(topic_base) + print(f"Subscribed to: {topic_base}") + else: + print(f"Failed to connect to MQTT for station {self.station_id}, return code {rc}") + + def on_disconnect(self, client, userdata, rc, properties): + """Callback for when the client disconnects.""" + print(f"Disconnected from MQTT for station {self.station_id}. Will attempt to reconnect...") + # Paho-MQTT's loop_start() handles automatic reconnection. + + def on_message(self, client, userdata, msg): + """Callback for when a message is received from the broker.""" + try: + # Pass the relevant data to the main application's handler + self.on_message_callback(self.station_id, msg.topic, msg.payload) + except Exception as e: + print(f"Error processing message in callback for topic {msg.topic}: {e}") + + def connect(self): + """Connects the client to the MQTT broker.""" + print(f"Attempting to connect to {self.broker}:{self.port} with client ID: {self.client_id}") + try: + self.client.connect(self.broker, self.port, 60) + except Exception as e: + print(f"Error connecting to MQTT for station {self.station_id}: {e}") + + def start(self): + """Starts the MQTT client's network loop in a separate thread.""" + self.connect() + self.client.loop_start() + + def stop(self): + """Stops the MQTT client's network loop.""" + print(f"Stopping MQTT client for station: {self.station_id}") + self.client.loop_stop() diff --git a/backend/core/protobuf_decoder.py b/backend/core/protobuf_decoder.py new file mode 100644 index 0000000..3f53323 --- /dev/null +++ b/backend/core/protobuf_decoder.py @@ -0,0 +1,157 @@ +import json +from google.protobuf.json_format import MessageToDict +from google.protobuf.message import DecodeError + +# Import the specific message types from your generated protobuf file +# Make sure the path 'proto.your_proto_file_pb2' matches your file structure +from proto.vec_payload_chgSt_pb2 import ( + mainPayload as PeriodicData, + eventPayload as EventData, + rpcRequest as RpcRequest, + eventType_e, + jobType_e, + languageType_e +) + +class ProtobufDecoder: + """ + Handles the decoding of different Protobuf message types with robust error handling. + """ + + def decode_periodic(self, payload: bytes) -> dict | None: + """ + Decodes a binary payload into a PeriodicData dictionary. + """ + try: + message = PeriodicData() + message.ParseFromString(payload) + return MessageToDict(message, preserving_proto_field_name=True) + except DecodeError as e: + print(f"Error decoding PeriodicData: {e}") + return None + except Exception as e: + print(f"An unexpected error occurred during periodic decoding: {e}") + return None + + def decode_event(self, payload_bytes: bytes) -> dict | None: + """ + Decodes an event payload robustly, ensuring the correct eventType is used. + """ + try: + # 1. Standard parsing to get a base dictionary + msg = EventData() + msg.ParseFromString(payload_bytes) + d = MessageToDict(msg, preserving_proto_field_name=True) + + # 2. Manually extract the true enum value from the raw bytes + wire_num = self._extract_field3_varint(payload_bytes) + wire_name = None + if wire_num is not None: + try: + wire_name = eventType_e.Name(wire_num) + except ValueError: + wire_name = f"UNKNOWN_ENUM_VALUE_{wire_num}" + + # 3. Always prefer the manually extracted "wire value" + if wire_name: + d["eventType"] = wire_name + + # 4. Ensure consistent structure with default values + ed = d.setdefault("eventData", {}) + ed.setdefault("nfcData", None) + ed.setdefault("batteryIdentification", "") + ed.setdefault("activityFailureReason", 0) + ed.setdefault("swapAbortReason", "ABORT_UNKNOWN") + ed.setdefault("swapTime", 0) + ed.setdefault("faultCode", 0) + ed.setdefault("doorStatus", 0) + ed.setdefault("slotId", 0) + + # 5. Reorder for clean logs and return as a dictionary + return { + "ts": d.get("ts"), + "deviceId": d.get("deviceId"), + "eventType": d.get("eventType"), + "sessionId": d.get("sessionId"), + "eventData": d.get("eventData"), + } + except Exception as e: + print(f"An unexpected error occurred during event decoding: {e}") + return None + + def decode_rpc_request(self, payload_bytes: bytes) -> dict | None: + """ + Decodes an RPC request payload robustly, ensuring the correct jobType is used. + """ + try: + # 1. Standard parsing + msg = RpcRequest() + msg.ParseFromString(payload_bytes) + d = MessageToDict(msg, preserving_proto_field_name=True) + + # 2. Manually extract the true enum value for jobType (field 3) + wire_num = self._extract_field3_varint(payload_bytes) + wire_name = None + if wire_num is not None: + try: + wire_name = jobType_e.Name(wire_num) + except ValueError: + wire_name = f"UNKNOWN_ENUM_VALUE_{wire_num}" + + # 3. Prefer the manually extracted value + if wire_name: + d["jobType"] = wire_name + + # 4. Ensure consistent structure + d.setdefault("rpcData", None) + d.setdefault("slotInfo", None) + + return d + except Exception as e: + print(f"An unexpected error occurred during RPC request decoding: {e}") + return None + + + # --- Helper methods for manual byte parsing --- + def _read_varint(self, b: bytes, i: int): + """Helper to read a varint from a raw byte buffer.""" + shift = 0 + val = 0 + while True: + if i >= len(b): raise ValueError("truncated varint") + c = b[i] + i += 1 + val |= (c & 0x7F) << shift + if not (c & 0x80): break + shift += 7 + if shift > 64: raise ValueError("varint too long") + return val, i + + def _skip_field(self, b: bytes, i: int, wt: int): + """Helper to skip a field in the buffer based on its wire type.""" + if wt == 0: # VARINT + _, i = self._read_varint(b, i) + return i + if wt == 1: # 64-BIT + return i + 8 + if wt == 2: # LENGTH-DELIMITED + ln, i = self._read_varint(b, i) + return i + ln + if wt == 5: # 32-BIT + return i + 4 + raise ValueError(f"unsupported wire type to skip: {wt}") + + def _extract_field3_varint(self, b: bytes): + """Manually parses the byte string to find the integer value of field number 3 (e.g., eventType, jobType).""" + i = 0 + n = len(b) + while i < n: + key, i2 = self._read_varint(b, i) + wt = key & 0x7 + fn = key >> 3 + i = i2 + if fn == 3 and wt == 0: + v, _ = self._read_varint(b, i) + return v + i = self._skip_field(b, i, wt) + return None diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000..e5be621 --- /dev/null +++ b/backend/main.py @@ -0,0 +1,436 @@ +# import os +# import sys +# import threading +# import json +# import csv +# import io +# from datetime import datetime +# from flask import Flask, jsonify, request, Response +# from flask_socketio import SocketIO +# from dotenv import load_dotenv + +# # Import your custom core modules and the new models +# from core.mqtt_client import MqttClient +# from core.protobuf_decoder import ProtobufDecoder +# from models import db, Station, User, MqttLog + +# # --- Load Environment Variables --- +# load_dotenv() + +# # --- Pre-startup Check for Essential Configuration --- +# DATABASE_URL = os.getenv("DATABASE_URL") +# if not DATABASE_URL: +# print("FATAL ERROR: DATABASE_URL is not set in .env file.") +# sys.exit(1) + +# # --- Application Setup --- +# app = Flask(__name__) +# app.config['SQLALCHEMY_DATABASE_URI'] = DATABASE_URL +# app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +# app.config['SECRET_KEY'] = os.getenv("SECRET_KEY", "a_very_secret_key") +# db.init_app(app) +# socketio = SocketIO(app, cors_allowed_origins="*") + +# # --- Global instances --- +# decoder = ProtobufDecoder() +# mqtt_clients = {} + +# # --- MQTT Message Handling --- +# def on_message_handler(station_id, topic, payload): +# """ +# Handles incoming MQTT messages, decodes them, writes to PostgreSQL, +# and emits to WebSockets. +# """ +# print(f"Main handler received message for station {station_id} on topic {topic}") + +# decoded_data = None +# message_type = topic.split('/')[-1] + +# if message_type == 'PERIODIC': +# decoded_data = decoder.decode_periodic(payload) +# elif message_type == 'EVENTS': +# decoded_data = decoder.decode_event(payload) +# elif message_type == 'REQUEST': +# decoded_data = decoder.decode_rpc_request(payload) + +# if decoded_data: +# # 1. Write the data to PostgreSQL for historical storage +# try: +# with app.app_context(): +# log_entry = MqttLog( +# station_id=station_id, +# topic=topic, +# payload=decoded_data +# ) +# db.session.add(log_entry) +# db.session.commit() +# print(f"Successfully wrote data for {station_id} to PostgreSQL.") +# except Exception as e: +# print(f"Error writing to PostgreSQL: {e}") + +# # 2. Emit the data to the frontend for real-time view +# socketio.emit('dashboard_update', { +# 'stationId': station_id, +# 'topic': topic, +# 'data': decoded_data +# }, room=station_id) + +# # --- (WebSocket and API routes remain the same) --- +# @socketio.on('connect') +# def handle_connect(): +# print('Client connected to WebSocket') + +# @socketio.on('disconnect') +# def handle_disconnect(): +# print('Client disconnected') + +# @socketio.on('join_station_room') +# def handle_join_station_room(data): +# station_id = data.get('station_id') +# if station_id: +# from flask import request +# socketio.join_room(station_id, request.sid) + +# @socketio.on('leave_station_room') +# def handle_leave_station_room(data): +# station_id = data.get('station_id') +# if station_id: +# from flask import request +# socketio.leave_room(station_id, request.sid) + +# @app.route('/api/stations', methods=['GET']) +# def get_stations(): +# try: +# stations = Station.query.all() +# return jsonify([{"id": s.station_id, "name": s.name} for s in stations]) +# except Exception as e: +# return jsonify({"error": f"Database query failed: {e}"}), 500 + +# # --- (CSV Export route remains the same) --- +# @app.route('/api/logs/export', methods=['GET']) +# def export_logs(): +# # ... (existing implementation) +# pass + +# # --- Main Application Logic (UPDATED) --- +# def start_mqtt_clients(): +# """ +# Initializes and starts an MQTT client for each station found in the database, +# using the specific MQTT credentials stored for each station. +# """ +# try: +# with app.app_context(): +# # Get the full station objects, not just the IDs +# stations = Station.query.all() +# except Exception as e: +# print(f"CRITICAL: Could not query stations from the database in MQTT thread: {e}") +# return + +# for station in stations: +# if station.station_id not in mqtt_clients: +# print(f"Creating and starting MQTT client for station: {station.name} ({station.station_id})") + +# # Use the specific details from each station object in the database +# client = MqttClient( +# broker=station.mqtt_broker, +# port=station.mqtt_port, +# user=station.mqtt_user, +# password=station.mqtt_password, +# station_id=station.station_id, +# on_message_callback=on_message_handler +# ) +# client.start() +# mqtt_clients[station.station_id] = client + +# if __name__ == '__main__': +# try: +# with app.app_context(): +# db.create_all() +# if not Station.query.first(): +# print("No stations found. Adding a default station with default MQTT config.") +# # Add a default station with MQTT details for first-time setup +# default_station = Station( +# station_id="V16000868210069259709", +# name="Test Station 2", +# mqtt_broker="mqtt-dev.upgrid.in", +# mqtt_port=1883, +# mqtt_user="guest", +# mqtt_password="password" +# ) +# db.session.add(default_station) +# db.session.commit() +# except Exception as e: +# print(f"FATAL ERROR: Could not connect to PostgreSQL: {e}") +# sys.exit(1) + +# mqtt_thread = threading.Thread(target=start_mqtt_clients, daemon=True) +# mqtt_thread.start() + +# print(f"Starting Flask-SocketIO server on http://localhost:5000") +# socketio.run(app, host='0.0.0.0', port=5000) + + + + + + + + + + + + + + + + + + + + + +import os +import sys +import threading +import json +import csv +import io +from datetime import datetime +from flask import Flask, jsonify, request, Response +from flask_socketio import SocketIO +from dotenv import load_dotenv + +# Import your custom core modules and the new models +from core.mqtt_client import MqttClient +from core.protobuf_decoder import ProtobufDecoder +from models import db, Station, User, MqttLog + +# --- Load Environment Variables --- +load_dotenv() + +# --- Pre-startup Check for Essential Configuration --- +DATABASE_URL = os.getenv("DATABASE_URL") +if not DATABASE_URL: + print("FATAL ERROR: DATABASE_URL is not set in .env file.") + sys.exit(1) + +# --- Application Setup --- +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = DATABASE_URL +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +app.config['SECRET_KEY'] = os.getenv("SECRET_KEY", "a_very_secret_key") +db.init_app(app) +socketio = SocketIO(app, cors_allowed_origins="*") + +# --- Global instances --- +decoder = ProtobufDecoder() +mqtt_clients = {} + +# --- MQTT Message Handling (UPDATED) --- +def on_message_handler(station_id, topic, payload): + """ + Handles incoming MQTT messages, decodes them, writes to PostgreSQL, + and emits to WebSockets. + """ + print(f"Main handler received message for station {station_id} on topic {topic}") + + decoded_data = None + message_type = topic.split('/')[-1] + + if message_type == 'PERIODIC': + decoded_data = decoder.decode_periodic(payload) + elif message_type == 'EVENTS': + decoded_data = decoder.decode_event(payload) + elif message_type == 'REQUEST': + decoded_data = decoder.decode_rpc_request(payload) + + if decoded_data: + # 1. Write the data to PostgreSQL for historical storage + try: + with app.app_context(): + log_entry = MqttLog( + station_id=station_id, + topic=topic, + topic_type=message_type, # <-- Save the new topic_type + payload=decoded_data + ) + db.session.add(log_entry) + db.session.commit() + print(f"Successfully wrote data for {station_id} to PostgreSQL.") + except Exception as e: + print(f"Error writing to PostgreSQL: {e}") + + # 2. Emit the data to the frontend for real-time view + socketio.emit('dashboard_update', { + 'stationId': station_id, + 'topic': topic, + 'data': decoded_data + }, room=station_id) + +# --- (WebSocket and API routes remain the same) --- +@socketio.on('connect') +def handle_connect(): + print('Client connected to WebSocket') + +# ... (other socketio handlers) + +@app.route('/api/stations', methods=['GET']) +def get_stations(): + try: + stations = Station.query.all() + return jsonify([{"id": s.station_id, "name": s.name} for s in stations]) + except Exception as e: + return jsonify({"error": f"Database query failed: {e}"}), 500 + +# --- CSV Export route (UPDATED) --- +def _format_periodic_row(payload, num_slots=9): + """ + Flattens a periodic payload dictionary into a single list for a CSV row. + """ + row = [ + datetime.fromtimestamp(payload.get("ts")).strftime("%Y-%m-%d %H:%M:%S"), + payload.get("deviceId", ""), + payload.get("stationDiagnosticCode", "") + ] + + slots_data = payload.get("slotLevelPayload", []) + slot_map = {s.get('slotId', i+1): s for i, s in enumerate(slots_data)} + + slot_fields_keys = [ + "batteryIdentification", "batteryPresent", "chargerPresent", "doorStatus", "doorLockStatus", + "voltage", "current", "soc", "batteryMaxTemp", "slotTemperature", + "batteryFaultCode", "chargerFaultCode", "batteryMode", "chargerMode" + ] + + for i in range(1, num_slots + 1): + slot = slot_map.get(i) + if slot: + row.extend([ + slot.get('batteryIdentification', ''), + slot.get("batteryPresent", 0), + slot.get("chargerPresent", 0), + slot.get("doorStatus", 0), + slot.get("doorLockStatus", 0), + slot.get('voltage', 0) / 1000.0, + slot.get('current', 0) / 1000.0, + slot.get('soc', 0), + slot.get('batteryMaxTemp', 0) / 10.0, + slot.get('slotTemperature', 0) / 10.0, + slot.get('batteryFaultCode', 0), + slot.get('chargerFaultCode', 0), + slot.get('batteryMode', 0), + slot.get('chargerMode', 0) + ]) + else: + row.extend([''] * len(slot_fields_keys)) + + row.append('') # Placeholder for RawHexPayload + return row + +@app.route('/api/logs/export', methods=['GET']) +def export_logs(): + station_id = request.args.get('station_id') + start_date_str = request.args.get('start_date') + end_date_str = request.args.get('end_date') + log_type = request.args.get('log_type', 'PERIODIC') + + if not all([station_id, start_date_str, end_date_str]): + return jsonify({"error": "Missing required parameters: station_id, start_date, end_date"}), 400 + + try: + start_date = datetime.strptime(start_date_str, '%Y-%m-%d') + end_date = datetime.strptime(end_date_str, '%Y-%m-%d').replace(hour=23, minute=59, second=59) + except ValueError: + return jsonify({"error": "Invalid date format. Use YYYY-MM-DD."}), 400 + + # UPDATED QUERY: Filter by the new 'topic_type' column for better performance + query = MqttLog.query.filter( + MqttLog.station_id == station_id, + MqttLog.timestamp.between(start_date, end_date), + MqttLog.topic_type == log_type + ) + + logs = query.order_by(MqttLog.timestamp.asc()).all() + + output = io.StringIO() + writer = csv.writer(output) + + if log_type == 'PERIODIC': + base_header = ["Timestamp", "DeviceID", "StationDiagnosticCode"] + slot_fields = [ + "BatteryID", "BatteryPresent", "ChargerPresent", "DoorStatus", "DoorLockStatus", + "Voltage_V", "Current_A", "SOC_Percent", "BatteryTemp_C", "SlotTemp_C", + "BatteryFaultCode", "ChargerFaultCode", "BatteryMode", "ChargerMode" + ] + slot_header = [f"Slot{i}_{field}" for i in range(1, 10) for field in slot_fields] + header = base_header + slot_header + ["RawHexPayload"] + writer.writerow(header) + + for log in logs: + writer.writerow(_format_periodic_row(log.payload)) + else: # For EVENTS_RPC + header = ["Timestamp", "Topic", "Payload_JSON"] + writer.writerow(header) + for log in logs: + writer.writerow([log.timestamp, log.topic, json.dumps(log.payload)]) + + output.seek(0) + return Response( + output, + mimetype="text/csv", + headers={"Content-Disposition": f"attachment;filename=logs_{station_id}_{log_type}_{start_date_str}_to_{end_date_str}.csv"} + ) + +# --- Main Application Logic --- +def start_mqtt_clients(): + """ + Initializes and starts an MQTT client for each station found in the database, + using the specific MQTT credentials stored for each station. + """ + try: + with app.app_context(): + stations = Station.query.all() + except Exception as e: + print(f"CRITICAL: Could not query stations from the database in MQTT thread: {e}") + return + + for station in stations: + if station.station_id not in mqtt_clients: + print(f"Creating and starting MQTT client for station: {station.name} ({station.station_id})") + + client = MqttClient( + broker=station.mqtt_broker, + port=station.mqtt_port, + user=station.mqtt_user, + password=station.mqtt_password, + station_id=station.station_id, + on_message_callback=on_message_handler + ) + client.start() + mqtt_clients[station.station_id] = client + +if __name__ == '__main__': + try: + with app.app_context(): + db.create_all() + if not Station.query.first(): + print("No stations found. Adding a default station with default MQTT config.") + default_station = Station( + station_id="V16000868210069259709", + name="Test Station 2", + mqtt_broker="mqtt-dev.upgrid.in", + mqtt_port=1883, + mqtt_user="guest", + mqtt_password="password" + ) + db.session.add(default_station) + db.session.commit() + except Exception as e: + print(f"FATAL ERROR: Could not connect to PostgreSQL: {e}") + sys.exit(1) + + mqtt_thread = threading.Thread(target=start_mqtt_clients, daemon=True) + mqtt_thread.start() + + print(f"Starting Flask-SocketIO server on http://localhost:5000") + socketio.run(app, host='0.0.0.0', port=5000) diff --git a/backend/models.py b/backend/models.py new file mode 100644 index 0000000..29be586 --- /dev/null +++ b/backend/models.py @@ -0,0 +1,35 @@ +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.dialects.postgresql import JSONB + +# Create a SQLAlchemy instance. This will be linked to the Flask app in main.py. +db = SQLAlchemy() + +class User(db.Model): + """Represents a user in the database.""" + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + password_hash = db.Column(db.String(120), nullable=False) + +class Station(db.Model): + id = db.Column(db.Integer, primary_key=True) + station_id = db.Column(db.String(120), unique=True, nullable=False) + name = db.Column(db.String(120), nullable=True) + location = db.Column(db.String(200), nullable=True) + + # --- ADD THESE NEW FIELDS --- + mqtt_broker = db.Column(db.String(255), nullable=False) + mqtt_port = db.Column(db.Integer, nullable=False) + mqtt_user = db.Column(db.String(120), nullable=True) + mqtt_password = db.Column(db.String(120), nullable=True) + +# --- Table for MQTT Logs (without raw payload) --- +class MqttLog(db.Model): + """Represents a single MQTT message payload for historical logging.""" + id = db.Column(db.Integer, primary_key=True) + timestamp = db.Column(db.DateTime, server_default=db.func.now()) + station_id = db.Column(db.String(120), nullable=False, index=True) + topic = db.Column(db.String(255), nullable=False) + # --- NEW: Added topic_type for efficient filtering --- + topic_type = db.Column(db.String(50), nullable=False, index=True) + # JSONB is a highly efficient way to store JSON data in PostgreSQL + payload = db.Column(JSONB) diff --git a/backend/proto/__init__.py b/backend/proto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/proto/__pycache__/__init__.cpython-313.pyc b/backend/proto/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25d384da07393c68bba8e51410728347050042b0 GIT binary patch literal 185 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 WXUi|C$=~Ss^K}n3bY8mwjrxB-ZWq}A literal 0 HcmV?d00001 diff --git a/backend/proto/vec_payload_chgSt.proto b/backend/proto/vec_payload_chgSt.proto new file mode 100644 index 0000000..521b42c --- /dev/null +++ b/backend/proto/vec_payload_chgSt.proto @@ -0,0 +1,162 @@ +syntax = "proto2"; + +enum eventType_e { + EVENT_SWAP_START = 0x200; + EVENT_BATTERY_ENTRY = 0x201; + EVENT_BATTERY_EXIT = 0x202; + EVENT_ACTIVITY_FAILED = 0x203; + EVENT_SWAP_ABORTED = 0x204; + EVENT_BATFAULT_ALARM = 0x205; + EVENT_SLOT_LOCK_ENEGAGED = 0x206; + EVENT_SWAP_ENDED = 0x207; + EVENT_CHGFAULT_ALARM = 0x208; + EVENT_NFC_SCAN = 0x209; + EVENT_SLOT_LOCK_DISENEGAGED = 0x20A; + EVENT_REVERSE_SWAP = 0x20B; +} + +enum jobType_e { + JOBTYPE_NONE = 0; + JOBTYPE_GET_STATUS_OF_A_JOB = 0x01; + JOBTYPE_SWAP_START = 0x100; + JOBTYPE_CHARGER_ENABLE_DISABLE = 0x101; + JOBTYPE_GATE_OPEN_CLOSE = 0x102; + JOBTYPE_TRANSACTION_ABORT = 0x103; + JOBTYPE_REBOOT = 0x104; + JOBTYPE_SWAP_DENY = 0x105; + JOBTYPE_LANGUAGE_UPDATE = 0x106; +} + +enum jobResult_e { + JOB_RESULT_UNKNOWN = 0; + JOB_RESULT_SUCCESS = 1; + JOB_RESULT_REJECTED = 2; + JOB_RESULT_TIMEOUT = 3; +} + +enum jobStatus_e { + JOB_STATUS_IDLE = 0; + JOB_STATUS_PENDING = 1; + JOB_STATUS_EXECUTING = 2; + JOB_STATUS_EXECUTED = 3; +} + +message slotLevelPayload{ + optional uint32 batteryPresent = 1; + optional uint32 chargerPresent = 2; + optional uint32 doorLockStatus = 3; + optional uint32 doorStatus = 4; + optional uint32 voltage = 5; + optional int32 current = 6; + optional uint32 batteryFaultCode = 7; + optional uint32 chargerFaultCode = 8; + optional int32 batteryMaxTemp = 9; + optional int32 chargerMaxTemp= 10; + optional string batteryIdentification = 11; + optional uint32 batteryMode = 12; + optional uint32 chargerMode = 13; + optional int32 slotTemperature = 14; + optional uint32 gasSensor = 15; + optional uint32 soc=16; + optional uint32 ts = 17; +} + +message mainPayload{ + required uint32 ts = 1; + required string deviceId = 2; + required string sessionId = 3; + repeated slotLevelPayload slotLevelPayload = 4; + optional uint32 backupSupplyStatus = 5; + repeated uint32 switchStatus = 6; + optional uint32 stationStatus = 7; + optional uint32 stationDiagnosticCode = 8; + repeated float coordinates = 9; +} + +enum swapAbortReason_e{ + ABORT_UNKNOWN=0; + ABORT_BAT_EXIT_TIMEOUT=1; + ABORT_BAT_ENTRY_TIMEOUT=2; + ABORT_DOOR_CLOSE_TIMEOUT=3; + ABORT_DOOR_OPEN_TIMEOUT=4; + ABORT_INVALID_PARAM=5; + ABORT_REMOTE_REQUESTED=6; + ABORT_INVALID_BATTERY=7; +} + +enum swapDenyReason_e{ + SWAP_DENY_INSUFFICIENT_BAL=1; + SWAP_DENY_INVALID_NFC=2; + SWAP_DENY_BATTERY_UNAVAILABLE=3; +} + +enum languageType_e{ + LANGUAGE_TYPE_ENGLISH = 1; + LANGUAGE_TYPE_HINDI = 2; + LANGUAGE_TYPE_KANNADA = 3; + LANGUAGE_TYPE_TELUGU = 4; +} + +message nfcPayload_s{ + required string manufacturingData = 1; + required string customData = 2; +} + +message eventData_s { + optional nfcPayload_s nfcData = 1; + optional string batteryIdentification = 2; + optional uint32 activityFailureReason = 3; + optional swapAbortReason_e swapAbortReason = 4; + optional uint32 swapTime = 5; + optional uint32 faultCode = 6; + optional uint32 doorStatus = 7; + optional uint32 slotId = 8; +} + +message eventPayload { + required uint32 ts = 1; + required string deviceId = 2; + required eventType_e eventType = 3; + required string sessionId = 4; + optional eventData_s eventData = 5; +} + +message rpcData_s { + optional string sessionId = 1; + repeated uint32 slotsData = 2; +} + +message slotControl_s { + required uint32 slotId = 1; + required uint32 state = 2; +} + +message getJobStatusByJobId_s{ + required string jobId = 1; +} + +message rpcRequest { + required uint32 ts = 1; + required string jobId = 2; + required jobType_e jobType = 3; + optional rpcData_s rpcData = 4; + optional slotControl_s slotInfo = 5; + optional swapDenyReason_e swapDeny = 8; + optional getJobStatusByJobId_s getJobStatusByJobId = 9; + optional languageType_e languageType = 10; +} + +message jobStatusByJobIdResponse_s{ + required string jobId = 1; + required jobStatus_e jobStatus = 2; + required jobResult_e jobResult = 3; +} + +message rpcResponse { + required uint32 ts = 1; + required string deviceId = 2; + required string jobId = 3; + required jobStatus_e jobStatus = 4; + required jobResult_e jobResult = 5; + optional jobStatusByJobIdResponse_s jobStatusByJobIdResponse = 6; +} \ No newline at end of file diff --git a/backend/proto/vec_payload_chgSt_pb2.py b/backend/proto/vec_payload_chgSt_pb2.py new file mode 100644 index 0000000..a18742f --- /dev/null +++ b/backend/proto/vec_payload_chgSt_pb2.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: vec_payload_chgSt.proto +# Protobuf Python Version: 6.32.0 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 32, + 0, + '', + 'vec_payload_chgSt.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17vec_payload_chgSt.proto\"\x82\x03\n\x10slotLevelPayload\x12\x16\n\x0e\x62\x61tteryPresent\x18\x01 \x01(\r\x12\x16\n\x0e\x63hargerPresent\x18\x02 \x01(\r\x12\x16\n\x0e\x64oorLockStatus\x18\x03 \x01(\r\x12\x12\n\ndoorStatus\x18\x04 \x01(\r\x12\x0f\n\x07voltage\x18\x05 \x01(\r\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x05\x12\x18\n\x10\x62\x61tteryFaultCode\x18\x07 \x01(\r\x12\x18\n\x10\x63hargerFaultCode\x18\x08 \x01(\r\x12\x16\n\x0e\x62\x61tteryMaxTemp\x18\t \x01(\x05\x12\x16\n\x0e\x63hargerMaxTemp\x18\n \x01(\x05\x12\x1d\n\x15\x62\x61tteryIdentification\x18\x0b \x01(\t\x12\x13\n\x0b\x62\x61tteryMode\x18\x0c \x01(\r\x12\x13\n\x0b\x63hargerMode\x18\r \x01(\r\x12\x17\n\x0fslotTemperature\x18\x0e \x01(\x05\x12\x11\n\tgasSensor\x18\x0f \x01(\r\x12\x0b\n\x03soc\x18\x10 \x01(\r\x12\n\n\x02ts\x18\x11 \x01(\r\"\xe8\x01\n\x0bmainPayload\x12\n\n\x02ts\x18\x01 \x02(\r\x12\x10\n\x08\x64\x65viceId\x18\x02 \x02(\t\x12\x11\n\tsessionId\x18\x03 \x02(\t\x12+\n\x10slotLevelPayload\x18\x04 \x03(\x0b\x32\x11.slotLevelPayload\x12\x1a\n\x12\x62\x61\x63kupSupplyStatus\x18\x05 \x01(\r\x12\x14\n\x0cswitchStatus\x18\x06 \x03(\r\x12\x15\n\rstationStatus\x18\x07 \x01(\r\x12\x1d\n\x15stationDiagnosticCode\x18\x08 \x01(\r\x12\x13\n\x0b\x63oordinates\x18\t \x03(\x02\"=\n\x0cnfcPayload_s\x12\x19\n\x11manufacturingData\x18\x01 \x02(\t\x12\x12\n\ncustomData\x18\x02 \x02(\t\"\xe1\x01\n\x0b\x65ventData_s\x12\x1e\n\x07nfcData\x18\x01 \x01(\x0b\x32\r.nfcPayload_s\x12\x1d\n\x15\x62\x61tteryIdentification\x18\x02 \x01(\t\x12\x1d\n\x15\x61\x63tivityFailureReason\x18\x03 \x01(\r\x12+\n\x0fswapAbortReason\x18\x04 \x01(\x0e\x32\x12.swapAbortReason_e\x12\x10\n\x08swapTime\x18\x05 \x01(\r\x12\x11\n\tfaultCode\x18\x06 \x01(\r\x12\x12\n\ndoorStatus\x18\x07 \x01(\r\x12\x0e\n\x06slotId\x18\x08 \x01(\r\"\x81\x01\n\x0c\x65ventPayload\x12\n\n\x02ts\x18\x01 \x02(\r\x12\x10\n\x08\x64\x65viceId\x18\x02 \x02(\t\x12\x1f\n\teventType\x18\x03 \x02(\x0e\x32\x0c.eventType_e\x12\x11\n\tsessionId\x18\x04 \x02(\t\x12\x1f\n\teventData\x18\x05 \x01(\x0b\x32\x0c.eventData_s\"1\n\trpcData_s\x12\x11\n\tsessionId\x18\x01 \x01(\t\x12\x11\n\tslotsData\x18\x02 \x03(\r\".\n\rslotControl_s\x12\x0e\n\x06slotId\x18\x01 \x02(\r\x12\r\n\x05state\x18\x02 \x02(\r\"&\n\x15getJobStatusByJobId_s\x12\r\n\x05jobId\x18\x01 \x02(\t\"\x84\x02\n\nrpcRequest\x12\n\n\x02ts\x18\x01 \x02(\r\x12\r\n\x05jobId\x18\x02 \x02(\t\x12\x1b\n\x07jobType\x18\x03 \x02(\x0e\x32\n.jobType_e\x12\x1b\n\x07rpcData\x18\x04 \x01(\x0b\x32\n.rpcData_s\x12 \n\x08slotInfo\x18\x05 \x01(\x0b\x32\x0e.slotControl_s\x12#\n\x08swapDeny\x18\x08 \x01(\x0e\x32\x11.swapDenyReason_e\x12\x33\n\x13getJobStatusByJobId\x18\t \x01(\x0b\x32\x16.getJobStatusByJobId_s\x12%\n\x0clanguageType\x18\n \x01(\x0e\x32\x0f.languageType_e\"m\n\x1ajobStatusByJobIdResponse_s\x12\r\n\x05jobId\x18\x01 \x02(\t\x12\x1f\n\tjobStatus\x18\x02 \x02(\x0e\x32\x0c.jobStatus_e\x12\x1f\n\tjobResult\x18\x03 \x02(\x0e\x32\x0c.jobResult_e\"\xbb\x01\n\x0brpcResponse\x12\n\n\x02ts\x18\x01 \x02(\r\x12\x10\n\x08\x64\x65viceId\x18\x02 \x02(\t\x12\r\n\x05jobId\x18\x03 \x02(\t\x12\x1f\n\tjobStatus\x18\x04 \x02(\x0e\x32\x0c.jobStatus_e\x12\x1f\n\tjobResult\x18\x05 \x02(\x0e\x32\x0c.jobResult_e\x12=\n\x18jobStatusByJobIdResponse\x18\x06 \x01(\x0b\x32\x1b.jobStatusByJobIdResponse_s*\xc8\x02\n\x0b\x65ventType_e\x12\x15\n\x10\x45VENT_SWAP_START\x10\x80\x04\x12\x18\n\x13\x45VENT_BATTERY_ENTRY\x10\x81\x04\x12\x17\n\x12\x45VENT_BATTERY_EXIT\x10\x82\x04\x12\x1a\n\x15\x45VENT_ACTIVITY_FAILED\x10\x83\x04\x12\x17\n\x12\x45VENT_SWAP_ABORTED\x10\x84\x04\x12\x19\n\x14\x45VENT_BATFAULT_ALARM\x10\x85\x04\x12\x1d\n\x18\x45VENT_SLOT_LOCK_ENEGAGED\x10\x86\x04\x12\x15\n\x10\x45VENT_SWAP_ENDED\x10\x87\x04\x12\x19\n\x14\x45VENT_CHGFAULT_ALARM\x10\x88\x04\x12\x13\n\x0e\x45VENT_NFC_SCAN\x10\x89\x04\x12 \n\x1b\x45VENT_SLOT_LOCK_DISENEGAGED\x10\x8a\x04\x12\x17\n\x12\x45VENT_REVERSE_SWAP\x10\x8b\x04*\x85\x02\n\tjobType_e\x12\x10\n\x0cJOBTYPE_NONE\x10\x00\x12\x1f\n\x1bJOBTYPE_GET_STATUS_OF_A_JOB\x10\x01\x12\x17\n\x12JOBTYPE_SWAP_START\x10\x80\x02\x12#\n\x1eJOBTYPE_CHARGER_ENABLE_DISABLE\x10\x81\x02\x12\x1c\n\x17JOBTYPE_GATE_OPEN_CLOSE\x10\x82\x02\x12\x1e\n\x19JOBTYPE_TRANSACTION_ABORT\x10\x83\x02\x12\x13\n\x0eJOBTYPE_REBOOT\x10\x84\x02\x12\x16\n\x11JOBTYPE_SWAP_DENY\x10\x85\x02\x12\x1c\n\x17JOBTYPE_LANGUAGE_UPDATE\x10\x86\x02*n\n\x0bjobResult_e\x12\x16\n\x12JOB_RESULT_UNKNOWN\x10\x00\x12\x16\n\x12JOB_RESULT_SUCCESS\x10\x01\x12\x17\n\x13JOB_RESULT_REJECTED\x10\x02\x12\x16\n\x12JOB_RESULT_TIMEOUT\x10\x03*m\n\x0bjobStatus_e\x12\x13\n\x0fJOB_STATUS_IDLE\x10\x00\x12\x16\n\x12JOB_STATUS_PENDING\x10\x01\x12\x18\n\x14JOB_STATUS_EXECUTING\x10\x02\x12\x17\n\x13JOB_STATUS_EXECUTED\x10\x03*\xea\x01\n\x11swapAbortReason_e\x12\x11\n\rABORT_UNKNOWN\x10\x00\x12\x1a\n\x16\x41\x42ORT_BAT_EXIT_TIMEOUT\x10\x01\x12\x1b\n\x17\x41\x42ORT_BAT_ENTRY_TIMEOUT\x10\x02\x12\x1c\n\x18\x41\x42ORT_DOOR_CLOSE_TIMEOUT\x10\x03\x12\x1b\n\x17\x41\x42ORT_DOOR_OPEN_TIMEOUT\x10\x04\x12\x17\n\x13\x41\x42ORT_INVALID_PARAM\x10\x05\x12\x1a\n\x16\x41\x42ORT_REMOTE_REQUESTED\x10\x06\x12\x19\n\x15\x41\x42ORT_INVALID_BATTERY\x10\x07*p\n\x10swapDenyReason_e\x12\x1e\n\x1aSWAP_DENY_INSUFFICIENT_BAL\x10\x01\x12\x19\n\x15SWAP_DENY_INVALID_NFC\x10\x02\x12!\n\x1dSWAP_DENY_BATTERY_UNAVAILABLE\x10\x03*y\n\x0elanguageType_e\x12\x19\n\x15LANGUAGE_TYPE_ENGLISH\x10\x01\x12\x17\n\x13LANGUAGE_TYPE_HINDI\x10\x02\x12\x19\n\x15LANGUAGE_TYPE_KANNADA\x10\x03\x12\x18\n\x14LANGUAGE_TYPE_TELUGU\x10\x04') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'vec_payload_chgSt_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_EVENTTYPE_E']._serialized_start=1778 + _globals['_EVENTTYPE_E']._serialized_end=2106 + _globals['_JOBTYPE_E']._serialized_start=2109 + _globals['_JOBTYPE_E']._serialized_end=2370 + _globals['_JOBRESULT_E']._serialized_start=2372 + _globals['_JOBRESULT_E']._serialized_end=2482 + _globals['_JOBSTATUS_E']._serialized_start=2484 + _globals['_JOBSTATUS_E']._serialized_end=2593 + _globals['_SWAPABORTREASON_E']._serialized_start=2596 + _globals['_SWAPABORTREASON_E']._serialized_end=2830 + _globals['_SWAPDENYREASON_E']._serialized_start=2832 + _globals['_SWAPDENYREASON_E']._serialized_end=2944 + _globals['_LANGUAGETYPE_E']._serialized_start=2946 + _globals['_LANGUAGETYPE_E']._serialized_end=3067 + _globals['_SLOTLEVELPAYLOAD']._serialized_start=28 + _globals['_SLOTLEVELPAYLOAD']._serialized_end=414 + _globals['_MAINPAYLOAD']._serialized_start=417 + _globals['_MAINPAYLOAD']._serialized_end=649 + _globals['_NFCPAYLOAD_S']._serialized_start=651 + _globals['_NFCPAYLOAD_S']._serialized_end=712 + _globals['_EVENTDATA_S']._serialized_start=715 + _globals['_EVENTDATA_S']._serialized_end=940 + _globals['_EVENTPAYLOAD']._serialized_start=943 + _globals['_EVENTPAYLOAD']._serialized_end=1072 + _globals['_RPCDATA_S']._serialized_start=1074 + _globals['_RPCDATA_S']._serialized_end=1123 + _globals['_SLOTCONTROL_S']._serialized_start=1125 + _globals['_SLOTCONTROL_S']._serialized_end=1171 + _globals['_GETJOBSTATUSBYJOBID_S']._serialized_start=1173 + _globals['_GETJOBSTATUSBYJOBID_S']._serialized_end=1211 + _globals['_RPCREQUEST']._serialized_start=1214 + _globals['_RPCREQUEST']._serialized_end=1474 + _globals['_JOBSTATUSBYJOBIDRESPONSE_S']._serialized_start=1476 + _globals['_JOBSTATUSBYJOBIDRESPONSE_S']._serialized_end=1585 + _globals['_RPCRESPONSE']._serialized_start=1588 + _globals['_RPCRESPONSE']._serialized_end=1775 +# @@protoc_insertion_point(module_scope) diff --git a/backend/proto/vec_payload_chgSt_pb2.pyi b/backend/proto/vec_payload_chgSt_pb2.pyi new file mode 100644 index 0000000..64a2210 --- /dev/null +++ b/backend/proto/vec_payload_chgSt_pb2.pyi @@ -0,0 +1,287 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class eventType_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + EVENT_SWAP_START: _ClassVar[eventType_e] + EVENT_BATTERY_ENTRY: _ClassVar[eventType_e] + EVENT_BATTERY_EXIT: _ClassVar[eventType_e] + EVENT_ACTIVITY_FAILED: _ClassVar[eventType_e] + EVENT_SWAP_ABORTED: _ClassVar[eventType_e] + EVENT_BATFAULT_ALARM: _ClassVar[eventType_e] + EVENT_SLOT_LOCK_ENEGAGED: _ClassVar[eventType_e] + EVENT_SWAP_ENDED: _ClassVar[eventType_e] + EVENT_CHGFAULT_ALARM: _ClassVar[eventType_e] + EVENT_NFC_SCAN: _ClassVar[eventType_e] + EVENT_SLOT_LOCK_DISENEGAGED: _ClassVar[eventType_e] + EVENT_REVERSE_SWAP: _ClassVar[eventType_e] + +class jobType_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + JOBTYPE_NONE: _ClassVar[jobType_e] + JOBTYPE_GET_STATUS_OF_A_JOB: _ClassVar[jobType_e] + JOBTYPE_SWAP_START: _ClassVar[jobType_e] + JOBTYPE_CHARGER_ENABLE_DISABLE: _ClassVar[jobType_e] + JOBTYPE_GATE_OPEN_CLOSE: _ClassVar[jobType_e] + JOBTYPE_TRANSACTION_ABORT: _ClassVar[jobType_e] + JOBTYPE_REBOOT: _ClassVar[jobType_e] + JOBTYPE_SWAP_DENY: _ClassVar[jobType_e] + JOBTYPE_LANGUAGE_UPDATE: _ClassVar[jobType_e] + +class jobResult_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + JOB_RESULT_UNKNOWN: _ClassVar[jobResult_e] + JOB_RESULT_SUCCESS: _ClassVar[jobResult_e] + JOB_RESULT_REJECTED: _ClassVar[jobResult_e] + JOB_RESULT_TIMEOUT: _ClassVar[jobResult_e] + +class jobStatus_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + JOB_STATUS_IDLE: _ClassVar[jobStatus_e] + JOB_STATUS_PENDING: _ClassVar[jobStatus_e] + JOB_STATUS_EXECUTING: _ClassVar[jobStatus_e] + JOB_STATUS_EXECUTED: _ClassVar[jobStatus_e] + +class swapAbortReason_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + ABORT_UNKNOWN: _ClassVar[swapAbortReason_e] + ABORT_BAT_EXIT_TIMEOUT: _ClassVar[swapAbortReason_e] + ABORT_BAT_ENTRY_TIMEOUT: _ClassVar[swapAbortReason_e] + ABORT_DOOR_CLOSE_TIMEOUT: _ClassVar[swapAbortReason_e] + ABORT_DOOR_OPEN_TIMEOUT: _ClassVar[swapAbortReason_e] + ABORT_INVALID_PARAM: _ClassVar[swapAbortReason_e] + ABORT_REMOTE_REQUESTED: _ClassVar[swapAbortReason_e] + ABORT_INVALID_BATTERY: _ClassVar[swapAbortReason_e] + +class swapDenyReason_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SWAP_DENY_INSUFFICIENT_BAL: _ClassVar[swapDenyReason_e] + SWAP_DENY_INVALID_NFC: _ClassVar[swapDenyReason_e] + SWAP_DENY_BATTERY_UNAVAILABLE: _ClassVar[swapDenyReason_e] + +class languageType_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + LANGUAGE_TYPE_ENGLISH: _ClassVar[languageType_e] + LANGUAGE_TYPE_HINDI: _ClassVar[languageType_e] + LANGUAGE_TYPE_KANNADA: _ClassVar[languageType_e] + LANGUAGE_TYPE_TELUGU: _ClassVar[languageType_e] +EVENT_SWAP_START: eventType_e +EVENT_BATTERY_ENTRY: eventType_e +EVENT_BATTERY_EXIT: eventType_e +EVENT_ACTIVITY_FAILED: eventType_e +EVENT_SWAP_ABORTED: eventType_e +EVENT_BATFAULT_ALARM: eventType_e +EVENT_SLOT_LOCK_ENEGAGED: eventType_e +EVENT_SWAP_ENDED: eventType_e +EVENT_CHGFAULT_ALARM: eventType_e +EVENT_NFC_SCAN: eventType_e +EVENT_SLOT_LOCK_DISENEGAGED: eventType_e +EVENT_REVERSE_SWAP: eventType_e +JOBTYPE_NONE: jobType_e +JOBTYPE_GET_STATUS_OF_A_JOB: jobType_e +JOBTYPE_SWAP_START: jobType_e +JOBTYPE_CHARGER_ENABLE_DISABLE: jobType_e +JOBTYPE_GATE_OPEN_CLOSE: jobType_e +JOBTYPE_TRANSACTION_ABORT: jobType_e +JOBTYPE_REBOOT: jobType_e +JOBTYPE_SWAP_DENY: jobType_e +JOBTYPE_LANGUAGE_UPDATE: jobType_e +JOB_RESULT_UNKNOWN: jobResult_e +JOB_RESULT_SUCCESS: jobResult_e +JOB_RESULT_REJECTED: jobResult_e +JOB_RESULT_TIMEOUT: jobResult_e +JOB_STATUS_IDLE: jobStatus_e +JOB_STATUS_PENDING: jobStatus_e +JOB_STATUS_EXECUTING: jobStatus_e +JOB_STATUS_EXECUTED: jobStatus_e +ABORT_UNKNOWN: swapAbortReason_e +ABORT_BAT_EXIT_TIMEOUT: swapAbortReason_e +ABORT_BAT_ENTRY_TIMEOUT: swapAbortReason_e +ABORT_DOOR_CLOSE_TIMEOUT: swapAbortReason_e +ABORT_DOOR_OPEN_TIMEOUT: swapAbortReason_e +ABORT_INVALID_PARAM: swapAbortReason_e +ABORT_REMOTE_REQUESTED: swapAbortReason_e +ABORT_INVALID_BATTERY: swapAbortReason_e +SWAP_DENY_INSUFFICIENT_BAL: swapDenyReason_e +SWAP_DENY_INVALID_NFC: swapDenyReason_e +SWAP_DENY_BATTERY_UNAVAILABLE: swapDenyReason_e +LANGUAGE_TYPE_ENGLISH: languageType_e +LANGUAGE_TYPE_HINDI: languageType_e +LANGUAGE_TYPE_KANNADA: languageType_e +LANGUAGE_TYPE_TELUGU: languageType_e + +class slotLevelPayload(_message.Message): + __slots__ = ("batteryPresent", "chargerPresent", "doorLockStatus", "doorStatus", "voltage", "current", "batteryFaultCode", "chargerFaultCode", "batteryMaxTemp", "chargerMaxTemp", "batteryIdentification", "batteryMode", "chargerMode", "slotTemperature", "gasSensor", "soc", "ts") + BATTERYPRESENT_FIELD_NUMBER: _ClassVar[int] + CHARGERPRESENT_FIELD_NUMBER: _ClassVar[int] + DOORLOCKSTATUS_FIELD_NUMBER: _ClassVar[int] + DOORSTATUS_FIELD_NUMBER: _ClassVar[int] + VOLTAGE_FIELD_NUMBER: _ClassVar[int] + CURRENT_FIELD_NUMBER: _ClassVar[int] + BATTERYFAULTCODE_FIELD_NUMBER: _ClassVar[int] + CHARGERFAULTCODE_FIELD_NUMBER: _ClassVar[int] + BATTERYMAXTEMP_FIELD_NUMBER: _ClassVar[int] + CHARGERMAXTEMP_FIELD_NUMBER: _ClassVar[int] + BATTERYIDENTIFICATION_FIELD_NUMBER: _ClassVar[int] + BATTERYMODE_FIELD_NUMBER: _ClassVar[int] + CHARGERMODE_FIELD_NUMBER: _ClassVar[int] + SLOTTEMPERATURE_FIELD_NUMBER: _ClassVar[int] + GASSENSOR_FIELD_NUMBER: _ClassVar[int] + SOC_FIELD_NUMBER: _ClassVar[int] + TS_FIELD_NUMBER: _ClassVar[int] + batteryPresent: int + chargerPresent: int + doorLockStatus: int + doorStatus: int + voltage: int + current: int + batteryFaultCode: int + chargerFaultCode: int + batteryMaxTemp: int + chargerMaxTemp: int + batteryIdentification: str + batteryMode: int + chargerMode: int + slotTemperature: int + gasSensor: int + soc: int + ts: int + def __init__(self, batteryPresent: _Optional[int] = ..., chargerPresent: _Optional[int] = ..., doorLockStatus: _Optional[int] = ..., doorStatus: _Optional[int] = ..., voltage: _Optional[int] = ..., current: _Optional[int] = ..., batteryFaultCode: _Optional[int] = ..., chargerFaultCode: _Optional[int] = ..., batteryMaxTemp: _Optional[int] = ..., chargerMaxTemp: _Optional[int] = ..., batteryIdentification: _Optional[str] = ..., batteryMode: _Optional[int] = ..., chargerMode: _Optional[int] = ..., slotTemperature: _Optional[int] = ..., gasSensor: _Optional[int] = ..., soc: _Optional[int] = ..., ts: _Optional[int] = ...) -> None: ... + +class mainPayload(_message.Message): + __slots__ = ("ts", "deviceId", "sessionId", "slotLevelPayload", "backupSupplyStatus", "switchStatus", "stationStatus", "stationDiagnosticCode", "coordinates") + TS_FIELD_NUMBER: _ClassVar[int] + DEVICEID_FIELD_NUMBER: _ClassVar[int] + SESSIONID_FIELD_NUMBER: _ClassVar[int] + SLOTLEVELPAYLOAD_FIELD_NUMBER: _ClassVar[int] + BACKUPSUPPLYSTATUS_FIELD_NUMBER: _ClassVar[int] + SWITCHSTATUS_FIELD_NUMBER: _ClassVar[int] + STATIONSTATUS_FIELD_NUMBER: _ClassVar[int] + STATIONDIAGNOSTICCODE_FIELD_NUMBER: _ClassVar[int] + COORDINATES_FIELD_NUMBER: _ClassVar[int] + ts: int + deviceId: str + sessionId: str + slotLevelPayload: _containers.RepeatedCompositeFieldContainer[slotLevelPayload] + backupSupplyStatus: int + switchStatus: _containers.RepeatedScalarFieldContainer[int] + stationStatus: int + stationDiagnosticCode: int + coordinates: _containers.RepeatedScalarFieldContainer[float] + def __init__(self, ts: _Optional[int] = ..., deviceId: _Optional[str] = ..., sessionId: _Optional[str] = ..., slotLevelPayload: _Optional[_Iterable[_Union[slotLevelPayload, _Mapping]]] = ..., backupSupplyStatus: _Optional[int] = ..., switchStatus: _Optional[_Iterable[int]] = ..., stationStatus: _Optional[int] = ..., stationDiagnosticCode: _Optional[int] = ..., coordinates: _Optional[_Iterable[float]] = ...) -> None: ... + +class nfcPayload_s(_message.Message): + __slots__ = ("manufacturingData", "customData") + MANUFACTURINGDATA_FIELD_NUMBER: _ClassVar[int] + CUSTOMDATA_FIELD_NUMBER: _ClassVar[int] + manufacturingData: str + customData: str + def __init__(self, manufacturingData: _Optional[str] = ..., customData: _Optional[str] = ...) -> None: ... + +class eventData_s(_message.Message): + __slots__ = ("nfcData", "batteryIdentification", "activityFailureReason", "swapAbortReason", "swapTime", "faultCode", "doorStatus", "slotId") + NFCDATA_FIELD_NUMBER: _ClassVar[int] + BATTERYIDENTIFICATION_FIELD_NUMBER: _ClassVar[int] + ACTIVITYFAILUREREASON_FIELD_NUMBER: _ClassVar[int] + SWAPABORTREASON_FIELD_NUMBER: _ClassVar[int] + SWAPTIME_FIELD_NUMBER: _ClassVar[int] + FAULTCODE_FIELD_NUMBER: _ClassVar[int] + DOORSTATUS_FIELD_NUMBER: _ClassVar[int] + SLOTID_FIELD_NUMBER: _ClassVar[int] + nfcData: nfcPayload_s + batteryIdentification: str + activityFailureReason: int + swapAbortReason: swapAbortReason_e + swapTime: int + faultCode: int + doorStatus: int + slotId: int + def __init__(self, nfcData: _Optional[_Union[nfcPayload_s, _Mapping]] = ..., batteryIdentification: _Optional[str] = ..., activityFailureReason: _Optional[int] = ..., swapAbortReason: _Optional[_Union[swapAbortReason_e, str]] = ..., swapTime: _Optional[int] = ..., faultCode: _Optional[int] = ..., doorStatus: _Optional[int] = ..., slotId: _Optional[int] = ...) -> None: ... + +class eventPayload(_message.Message): + __slots__ = ("ts", "deviceId", "eventType", "sessionId", "eventData") + TS_FIELD_NUMBER: _ClassVar[int] + DEVICEID_FIELD_NUMBER: _ClassVar[int] + EVENTTYPE_FIELD_NUMBER: _ClassVar[int] + SESSIONID_FIELD_NUMBER: _ClassVar[int] + EVENTDATA_FIELD_NUMBER: _ClassVar[int] + ts: int + deviceId: str + eventType: eventType_e + sessionId: str + eventData: eventData_s + def __init__(self, ts: _Optional[int] = ..., deviceId: _Optional[str] = ..., eventType: _Optional[_Union[eventType_e, str]] = ..., sessionId: _Optional[str] = ..., eventData: _Optional[_Union[eventData_s, _Mapping]] = ...) -> None: ... + +class rpcData_s(_message.Message): + __slots__ = ("sessionId", "slotsData") + SESSIONID_FIELD_NUMBER: _ClassVar[int] + SLOTSDATA_FIELD_NUMBER: _ClassVar[int] + sessionId: str + slotsData: _containers.RepeatedScalarFieldContainer[int] + def __init__(self, sessionId: _Optional[str] = ..., slotsData: _Optional[_Iterable[int]] = ...) -> None: ... + +class slotControl_s(_message.Message): + __slots__ = ("slotId", "state") + SLOTID_FIELD_NUMBER: _ClassVar[int] + STATE_FIELD_NUMBER: _ClassVar[int] + slotId: int + state: int + def __init__(self, slotId: _Optional[int] = ..., state: _Optional[int] = ...) -> None: ... + +class getJobStatusByJobId_s(_message.Message): + __slots__ = ("jobId",) + JOBID_FIELD_NUMBER: _ClassVar[int] + jobId: str + def __init__(self, jobId: _Optional[str] = ...) -> None: ... + +class rpcRequest(_message.Message): + __slots__ = ("ts", "jobId", "jobType", "rpcData", "slotInfo", "swapDeny", "getJobStatusByJobId", "languageType") + TS_FIELD_NUMBER: _ClassVar[int] + JOBID_FIELD_NUMBER: _ClassVar[int] + JOBTYPE_FIELD_NUMBER: _ClassVar[int] + RPCDATA_FIELD_NUMBER: _ClassVar[int] + SLOTINFO_FIELD_NUMBER: _ClassVar[int] + SWAPDENY_FIELD_NUMBER: _ClassVar[int] + GETJOBSTATUSBYJOBID_FIELD_NUMBER: _ClassVar[int] + LANGUAGETYPE_FIELD_NUMBER: _ClassVar[int] + ts: int + jobId: str + jobType: jobType_e + rpcData: rpcData_s + slotInfo: slotControl_s + swapDeny: swapDenyReason_e + getJobStatusByJobId: getJobStatusByJobId_s + languageType: languageType_e + def __init__(self, ts: _Optional[int] = ..., jobId: _Optional[str] = ..., jobType: _Optional[_Union[jobType_e, str]] = ..., rpcData: _Optional[_Union[rpcData_s, _Mapping]] = ..., slotInfo: _Optional[_Union[slotControl_s, _Mapping]] = ..., swapDeny: _Optional[_Union[swapDenyReason_e, str]] = ..., getJobStatusByJobId: _Optional[_Union[getJobStatusByJobId_s, _Mapping]] = ..., languageType: _Optional[_Union[languageType_e, str]] = ...) -> None: ... + +class jobStatusByJobIdResponse_s(_message.Message): + __slots__ = ("jobId", "jobStatus", "jobResult") + JOBID_FIELD_NUMBER: _ClassVar[int] + JOBSTATUS_FIELD_NUMBER: _ClassVar[int] + JOBRESULT_FIELD_NUMBER: _ClassVar[int] + jobId: str + jobStatus: jobStatus_e + jobResult: jobResult_e + def __init__(self, jobId: _Optional[str] = ..., jobStatus: _Optional[_Union[jobStatus_e, str]] = ..., jobResult: _Optional[_Union[jobResult_e, str]] = ...) -> None: ... + +class rpcResponse(_message.Message): + __slots__ = ("ts", "deviceId", "jobId", "jobStatus", "jobResult", "jobStatusByJobIdResponse") + TS_FIELD_NUMBER: _ClassVar[int] + DEVICEID_FIELD_NUMBER: _ClassVar[int] + JOBID_FIELD_NUMBER: _ClassVar[int] + JOBSTATUS_FIELD_NUMBER: _ClassVar[int] + JOBRESULT_FIELD_NUMBER: _ClassVar[int] + JOBSTATUSBYJOBIDRESPONSE_FIELD_NUMBER: _ClassVar[int] + ts: int + deviceId: str + jobId: str + jobStatus: jobStatus_e + jobResult: jobResult_e + jobStatusByJobIdResponse: jobStatusByJobIdResponse_s + def __init__(self, ts: _Optional[int] = ..., deviceId: _Optional[str] = ..., jobId: _Optional[str] = ..., jobStatus: _Optional[_Union[jobStatus_e, str]] = ..., jobResult: _Optional[_Union[jobResult_e, str]] = ..., jobStatusByJobIdResponse: _Optional[_Union[jobStatusByJobIdResponse_s, _Mapping]] = ...) -> None: ... diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..56eeba8 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,7 @@ +Flask +Flask-SocketIO +Flask-SQLAlchemy +psycopg2-binary +paho-mqtt +protobuf +python-dotenv \ No newline at end of file