Skip to content

Commit 9124252

Browse files
author
Tom Softreck
committed
update
1 parent f1290ea commit 9124252

2 files changed

Lines changed: 94 additions & 121 deletions

File tree

modapi/api/cmd.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ def create_response(command: str) -> Dict[str, Any]:
2626
'success': False,
2727
'timestamp': None,
2828
'operation': None,
29-
'error': None
29+
'error': None,
30+
'port': None, # Add port field to response
31+
'baudrate': None # Add baudrate field to response
3032
}
3133

3234
def output_json(data: Dict[str, Any]):
@@ -70,6 +72,7 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
7072
else:
7173
response['port_source'] = 'command_line'
7274

75+
# Add port to response
7376
response['port'] = port
7477

7578
# Initialize modbus client
@@ -80,6 +83,9 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
8083
verbose=verbose
8184
)
8285

86+
# Add baudrate to response
87+
response['baudrate'] = modbus.baudrate
88+
8389
if not modbus.connect():
8490
response['error'] = f"Failed to connect to port {port}"
8591
return False, response
@@ -105,7 +111,7 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
105111
'register_type': 'coil'
106112
})
107113

108-
result = modbus.read_coils(address, count, unit)
114+
result = modbus.read_coils(address, count, unit=unit) # Use unit as kwarg
109115
if result is not None:
110116
response.update({
111117
'success': True,
@@ -136,7 +142,7 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
136142
'register_type': 'coil'
137143
})
138144

139-
if modbus.write_coil(address, value, unit):
145+
if modbus.write_coil(address, value, unit=unit): # Use unit as kwarg
140146
response.update({
141147
'success': True,
142148
'message': f"Coil {address} set to {'ON' if value else 'OFF'}",
@@ -165,7 +171,7 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
165171
'register_type': 'discrete_input'
166172
})
167173

168-
result = modbus.read_discrete_inputs(address, count, unit)
174+
result = modbus.read_discrete_inputs(address, count, unit=unit) # Use unit as kwarg
169175
if result is not None:
170176
response.update({
171177
'success': True,
@@ -196,16 +202,16 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
196202
'register_type': 'holding_register'
197203
})
198204

199-
result = modbus.read_holding_registers(address, count, unit)
205+
result = modbus.read_holding_registers(address, count, unit=unit) # Use unit as kwarg
200206
if result is not None:
201207
response.update({
202208
'success': True,
203209
'data': {
204210
'address': address,
205211
'count': count,
206212
'values': result,
207-
'values_dict': {str(i): val for i, val in enumerate(result, address)},
208-
'hex_values': [f"0x{val:04X}" for val in result]
213+
'values_hex': [hex(v) for v in result],
214+
'values_bin': [bin(v) for v in result]
209215
},
210216
'message': f"Read {count} holding registers starting at address {address}"
211217
})
@@ -224,19 +230,21 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
224230
response.update({
225231
'address': address,
226232
'value': value,
227-
'value_hex': f"0x{value:04X}",
233+
'value_hex': hex(value),
234+
'value_bin': bin(value),
228235
'unit': unit,
229236
'register_type': 'holding_register'
230237
})
231238

232-
if modbus.write_register(address, value, unit):
239+
if modbus.write_register(address, value, unit=unit): # Use unit as kwarg
233240
response.update({
234241
'success': True,
235-
'message': f"Register {address} set to {value} (0x{value:04X})",
242+
'message': f"Register {address} set to {value}",
236243
'data': {
237244
'address': address,
238245
'value': value,
239-
'value_hex': f"0x{value:04X}"
246+
'value_hex': hex(value),
247+
'value_bin': bin(value)
240248
}
241249
})
242250
else:
@@ -247,7 +255,7 @@ def execute_command(command: str, args: List[str], port: Optional[str] = None,
247255
return False, response
248256

249257
finally:
250-
if 'modbus' in locals():
258+
if 'modbus' in locals() and hasattr(modbus, 'disconnect'):
251259
modbus.disconnect()
252260

253261
# Return success status

modapi/api/rest.py

Lines changed: 74 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def connect_modbus():
7373
def add_cors_headers(response):
7474
"""Add CORS headers to allow cross-origin requests"""
7575
response.headers['Access-Control-Allow-Origin'] = '*'
76-
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
76+
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
7777
response.headers['Access-Control-Allow-Headers'] = 'Content-Type'
7878
return response
7979

@@ -101,7 +101,7 @@ def get_status():
101101
def read_coil(address):
102102
"""Read single coil"""
103103
unit = request.args.get('unit', default=1, type=int)
104-
result = modbus_client.read_coils(address, 1, unit)
104+
result = modbus_client.read_coils(address, 1, unit=unit)
105105

106106
if result is None:
107107
return jsonify({'error': 'Failed to read coil'}), 500
@@ -117,7 +117,7 @@ def read_coil(address):
117117
def read_coils(address, count):
118118
"""Read multiple coils"""
119119
unit = request.args.get('unit', default=1, type=int)
120-
result = modbus_client.read_coils(address, count, unit)
120+
result = modbus_client.read_coils(address, count, unit=unit)
121121

122122
if result is None:
123123
return jsonify({'error': 'Failed to read coils'}), 500
@@ -130,7 +130,7 @@ def read_coils(address, count):
130130
'unit': unit
131131
})
132132

133-
@app.route('/api/coils/<int:address>', methods=['POST'])
133+
@app.route('/api/coils/<int:address>', methods=['POST', 'PUT'])
134134
def write_coil(address):
135135
"""Write single coil"""
136136
data = request.get_json()
@@ -172,30 +172,47 @@ def toggle_coil(address):
172172
# Read current state
173173
result = modbus_client.read_coils(address, 1, unit=unit)
174174
if result is None:
175-
return jsonify({'error': 'Failed to read coil'}), 500
175+
return jsonify({'error': f'Failed to read coil {address}'}), 500
176176

177-
# Toggle state
178-
current_state = result[0]
179-
new_state = not current_state
177+
# Toggle value
178+
current_value = result[0]
179+
new_value = not current_value
180180

181-
if modbus_client.write_coil(address, new_state, unit=unit):
181+
# Write new value
182+
if modbus_client.write_coil(address, new_value, unit=unit):
182183
return jsonify({
183184
'success': True,
184185
'address': address,
185-
'previous': current_state,
186-
'current': new_state,
187-
'value': new_state,
188-
'value_display': 'ON' if new_state else 'OFF',
186+
'previous_value': current_value,
187+
'previous_value_display': 'ON' if current_value else 'OFF',
188+
'new_value': new_value,
189+
'new_value_display': 'ON' if new_value else 'OFF',
189190
'unit': unit
190191
})
191192
else:
192-
return jsonify({'error': f'Failed to write coil {address}'}), 500
193+
return jsonify({'error': f'Failed to toggle coil {address}'}), 500
194+
195+
@app.route('/api/discrete_inputs/<int:address>', methods=['GET'])
196+
def read_discrete_input(address):
197+
"""Read single discrete input"""
198+
unit = request.args.get('unit', default=1, type=int)
199+
result = modbus_client.read_discrete_inputs(address, 1, unit=unit)
200+
201+
if result is None:
202+
return jsonify({'error': 'Failed to read discrete input'}), 500
203+
204+
return jsonify({
205+
'address': address,
206+
'value': result[0],
207+
'value_display': 'ON' if result[0] else 'OFF',
208+
'unit': unit
209+
})
193210

194211
@app.route('/api/discrete_inputs/<int:address>/<int:count>', methods=['GET'])
195212
def read_discrete_inputs(address, count):
196-
"""Read discrete inputs"""
213+
"""Read multiple discrete inputs"""
197214
unit = request.args.get('unit', default=1, type=int)
198-
result = modbus_client.read_discrete_inputs(address, count, unit)
215+
result = modbus_client.read_discrete_inputs(address, count, unit=unit)
199216

200217
if result is None:
201218
return jsonify({'error': 'Failed to read discrete inputs'}), 500
@@ -208,11 +225,27 @@ def read_discrete_inputs(address, count):
208225
'unit': unit
209226
})
210227

228+
@app.route('/api/holding_registers/<int:address>', methods=['GET'])
229+
def read_holding_register(address):
230+
"""Read single holding register"""
231+
unit = request.args.get('unit', default=1, type=int)
232+
result = modbus_client.read_holding_registers(address, 1, unit=unit)
233+
234+
if result is None:
235+
return jsonify({'error': 'Failed to read holding register'}), 500
236+
237+
return jsonify({
238+
'address': address,
239+
'value': result[0],
240+
'value_hex': hex(result[0]),
241+
'unit': unit
242+
})
243+
211244
@app.route('/api/holding_registers/<int:address>/<int:count>', methods=['GET'])
212245
def read_holding_registers(address, count):
213-
"""Read holding registers"""
246+
"""Read multiple holding registers"""
214247
unit = request.args.get('unit', default=1, type=int)
215-
result = modbus_client.read_holding_registers(address, count, unit)
248+
result = modbus_client.read_holding_registers(address, count, unit=unit)
216249

217250
if result is None:
218251
return jsonify({'error': 'Failed to read holding registers'}), 500
@@ -221,14 +254,13 @@ def read_holding_registers(address, count):
221254
'address': address,
222255
'count': count,
223256
'values': result,
224-
'values_dict': {str(i): val for i, val in enumerate(result, address)},
225-
'hex_values': [f"0x{val:04X}" for val in result],
257+
'values_hex': [hex(v) for v in result],
226258
'unit': unit
227259
})
228260

229-
@app.route('/api/holding_registers/<int:address>', methods=['POST'])
261+
@app.route('/api/holding_registers/<int:address>', methods=['POST', 'PUT'])
230262
def write_holding_register(address):
231-
"""Write holding register"""
263+
"""Write single holding register"""
232264
data = request.get_json()
233265
if data is None:
234266
return jsonify({'error': 'Invalid JSON data'}), 400
@@ -244,17 +276,33 @@ def write_holding_register(address):
244276
'success': True,
245277
'address': address,
246278
'value': value,
247-
'value_hex': f"0x{value:04X}",
279+
'value_hex': hex(value),
248280
'unit': unit
249281
})
250282
else:
251283
return jsonify({'error': f'Failed to write register {address}'}), 500
252284

285+
@app.route('/api/input_registers/<int:address>', methods=['GET'])
286+
def read_input_register(address):
287+
"""Read single input register"""
288+
unit = request.args.get('unit', default=1, type=int)
289+
result = modbus_client.read_input_registers(address, 1, unit=unit)
290+
291+
if result is None:
292+
return jsonify({'error': 'Failed to read input register'}), 500
293+
294+
return jsonify({
295+
'address': address,
296+
'value': result[0],
297+
'value_hex': hex(result[0]),
298+
'unit': unit
299+
})
300+
253301
@app.route('/api/input_registers/<int:address>/<int:count>', methods=['GET'])
254302
def read_input_registers(address, count):
255-
"""Read input registers"""
303+
"""Read multiple input registers"""
256304
unit = request.args.get('unit', default=1, type=int)
257-
result = modbus_client.read_input_registers(address, count, unit)
305+
result = modbus_client.read_input_registers(address, count, unit=unit)
258306

259307
if result is None:
260308
return jsonify({'error': 'Failed to read input registers'}), 500
@@ -263,91 +311,8 @@ def read_input_registers(address, count):
263311
'address': address,
264312
'count': count,
265313
'values': result,
266-
'values_dict': {str(i): val for i, val in enumerate(result, address)},
267-
'hex_values': [f"0x{val:04X}" for val in result],
314+
'values_hex': [hex(v) for v in result],
268315
'unit': unit
269316
})
270317

271-
@app.route('/api/scan', methods=['GET'])
272-
def scan_devices():
273-
"""Scan for Modbus devices"""
274-
port = auto_detect_modbus_port()
275-
return jsonify({
276-
'success': port is not None,
277-
'port': port
278-
})
279-
280-
@app.route('/api/docs', methods=['GET'])
281-
def get_docs():
282-
"""Get API documentation"""
283-
return jsonify({
284-
'endpoints': [
285-
{
286-
'path': '/api/status',
287-
'method': 'GET',
288-
'description': 'Get Modbus connection status'
289-
},
290-
{
291-
'path': '/api/coils/<address>',
292-
'method': 'GET',
293-
'description': 'Read single coil',
294-
'params': ['unit (query, optional)']
295-
},
296-
{
297-
'path': '/api/coils/<address>/<count>',
298-
'method': 'GET',
299-
'description': 'Read multiple coils',
300-
'params': ['unit (query, optional)']
301-
},
302-
{
303-
'path': '/api/coils/<address>',
304-
'method': 'POST',
305-
'description': 'Write single coil',
306-
'body': {'value': 'boolean/int/string', 'unit': 'int (optional)'}
307-
},
308-
{
309-
'path': '/api/toggle/<address>',
310-
'method': 'POST',
311-
'description': 'Toggle coil state',
312-
'body': {'unit': 'int (optional)'}
313-
},
314-
{
315-
'path': '/api/discrete_inputs/<address>/<count>',
316-
'method': 'GET',
317-
'description': 'Read discrete inputs',
318-
'params': ['unit (query, optional)']
319-
},
320-
{
321-
'path': '/api/holding_registers/<address>/<count>',
322-
'method': 'GET',
323-
'description': 'Read holding registers',
324-
'params': ['unit (query, optional)']
325-
},
326-
{
327-
'path': '/api/holding_registers/<address>',
328-
'method': 'POST',
329-
'description': 'Write holding register',
330-
'body': {'value': 'int', 'unit': 'int (optional)'}
331-
},
332-
{
333-
'path': '/api/input_registers/<address>/<count>',
334-
'method': 'GET',
335-
'description': 'Read input registers',
336-
'params': ['unit (query, optional)']
337-
},
338-
{
339-
'path': '/api/scan',
340-
'method': 'GET',
341-
'description': 'Scan for Modbus devices'
342-
}
343-
]
344-
})
345-
346-
def run_server():
347-
"""Run the Flask server"""
348-
app.run(host=host, port=api_port, debug=debug)
349-
350-
# Add run method to app
351-
app.run_server = run_server
352-
353318
return app

0 commit comments

Comments
 (0)