From a221cd04b74f1feea4b02fb59dbdc41414394cb1 Mon Sep 17 00:00:00 2001 From: Geens Date: Wed, 7 Aug 2024 12:45:46 +0200 Subject: [PATCH] Added DN23E08 arduino code and added chaser.py --- DN23E08/DN23E08.ino | 30 +++++ DN23E08/coils.cpp | 125 ++++++++++++++++++ DN23E08/coils.h | 63 +++++++++ DN23E08/discrete_inputs.cpp | 61 +++++++++ DN23E08/discrete_inputs.h | 46 +++++++ DN23E08/input_registers.cpp | 50 +++++++ DN23E08/input_registers.h | 29 ++++ src/__init__.py | 3 +- src/chaser.py | 65 +++++++++ src/hardware.py | 28 +++- src/logic.py | 12 +- ...s_timers_and_relays.py => timed_relays.py} | 11 +- src/util.py | 2 +- uno_relay_modbus.ino | 70 ---------- 14 files changed, 511 insertions(+), 84 deletions(-) create mode 100644 DN23E08/DN23E08.ino create mode 100644 DN23E08/coils.cpp create mode 100644 DN23E08/coils.h create mode 100644 DN23E08/discrete_inputs.cpp create mode 100644 DN23E08/discrete_inputs.h create mode 100644 DN23E08/input_registers.cpp create mode 100644 DN23E08/input_registers.h create mode 100644 src/chaser.py rename src/{buttons_timers_and_relays.py => timed_relays.py} (93%) delete mode 100644 uno_relay_modbus.ino diff --git a/DN23E08/DN23E08.ino b/DN23E08/DN23E08.ino new file mode 100644 index 0000000..3a3fa8d --- /dev/null +++ b/DN23E08/DN23E08.ino @@ -0,0 +1,30 @@ +#include +#include "coils.h" +#include "discrete_inputs.h" +#include "input_registers.h" + +#define BAUD_RATE 57600 +#define SLAVE_ID 1 + +ModbusSerial mb (Serial, SLAVE_ID); +uint8_t led; + +void setup() { + Serial.begin (BAUD_RATE); + while (! Serial); + mb.config(BAUD_RATE); + setupCoils(mb); + setupDiscreteInputs(mb); + setupInputRegisters(mb); + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + digitalWrite(LED_BUILTIN, led > 128); + led++; + + discreteInputsTask(mb); + inputRegistersTask(mb); + mb.task(); + coilsTask(mb); +} \ No newline at end of file diff --git a/DN23E08/coils.cpp b/DN23E08/coils.cpp new file mode 100644 index 0000000..1425856 --- /dev/null +++ b/DN23E08/coils.cpp @@ -0,0 +1,125 @@ +#include "coils.h" + +void mbToCoils(ModbusSerial& mb); +void coilsToShiftOutRegisters(size_t i); +void outputShiftOutRegisters(); + +Coils coils; +ShiftOutRegistersUnion shiftOutRegisters; +size_t digitIndex; + +void setupCoils(ModbusSerial& mb) { + pinMode(DATA_PIN_595, OUTPUT); + pinMode(OE_PIN_595, OUTPUT); + pinMode(LATCH_PIN_595, OUTPUT); + pinMode(CLOCK_PIN_595, OUTPUT); + + digitalWrite(OE_PIN_595, LOW); + + for (size_t i = 0; i < COIL_COUNT; i++) + mb.addCoil (i); +} + +void coilsTask(ModbusSerial& mb) { + mbToCoils(mb); + coilsToShiftOutRegisters(digitIndex); + digitIndex = (digitIndex + 1) % 4; + outputShiftOutRegisters(); +} + +void mbToCoils(ModbusSerial& mb) { + coils.relays.J1 = mb.coil(0); + coils.relays.J2 = mb.coil(1); + coils.relays.J3 = mb.coil(2); + coils.relays.J4 = mb.coil(3); + coils.relays.J5 = mb.coil(4); + coils.relays.J6 = mb.coil(5); + coils.relays.J7 = mb.coil(6); + coils.relays.J8 = mb.coil(7); + + coils.digit0.A = mb.coil(8); + coils.digit0.B = mb.coil(9); + coils.digit0.C = mb.coil(10); + coils.digit0.D = mb.coil(11); + coils.digit0.E = mb.coil(12); + coils.digit0.F = mb.coil(13); + coils.digit0.G = mb.coil(14); + coils.digit0.H = mb.coil(15); + + coils.digit1.A = mb.coil(16); + coils.digit1.B = mb.coil(17); + coils.digit1.C = mb.coil(18); + coils.digit1.D = mb.coil(19); + coils.digit1.E = mb.coil(20); + coils.digit1.F = mb.coil(21); + coils.digit1.G = mb.coil(22); + coils.digit1.H = mb.coil(23); + + coils.digit2.A = mb.coil(24); + coils.digit2.B = mb.coil(25); + coils.digit2.C = mb.coil(26); + coils.digit2.D = mb.coil(27); + coils.digit2.E = mb.coil(28); + coils.digit2.F = mb.coil(29); + coils.digit2.G = mb.coil(30); + coils.digit2.H = mb.coil(31); + + coils.digit3.A = mb.coil(32); + coils.digit3.B = mb.coil(33); + coils.digit3.C = mb.coil(34); + coils.digit3.D = mb.coil(35); + coils.digit3.E = mb.coil(36); + coils.digit3.F = mb.coil(37); + coils.digit3.G = mb.coil(38); + coils.digit3.H = mb.coil(39); +} + +void coilsToShiftOutRegisters(size_t i) { + switch(i) { + case 0: + shiftOutRegisters.shiftOutRegisters.u6 = coils.digit0; + shiftOutRegisters.shiftOutRegisters.u8.G1 = 0; + shiftOutRegisters.shiftOutRegisters.u8.G2 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G3 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G4 = 1; + shiftOutRegisters.shiftOutRegisters.u5 = coils.relays; + break; + case 1: + shiftOutRegisters.shiftOutRegisters.u6 = coils.digit1; + shiftOutRegisters.shiftOutRegisters.u8.G1 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G2 = 0; + shiftOutRegisters.shiftOutRegisters.u8.G3 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G4 = 1; + shiftOutRegisters.shiftOutRegisters.u5 = coils.relays; + break; + case 2: + shiftOutRegisters.shiftOutRegisters.u6 = coils.digit2; + shiftOutRegisters.shiftOutRegisters.u8.G1 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G2 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G3 = 0; + shiftOutRegisters.shiftOutRegisters.u8.G4 = 1; + shiftOutRegisters.shiftOutRegisters.u5 = coils.relays; + break; + default: + shiftOutRegisters.shiftOutRegisters.u6 = coils.digit3; + shiftOutRegisters.shiftOutRegisters.u8.G1 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G2 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G3 = 1; + shiftOutRegisters.shiftOutRegisters.u8.G4 = 0; + shiftOutRegisters.shiftOutRegisters.u5 = coils.relays; + break; + } +} + +void outputShiftOutRegisters() { + digitalWrite(LATCH_PIN_595, LOW); + shiftOut(DATA_PIN_595, CLOCK_PIN_595, MSBFIRST, shiftOutRegisters.bytes[2]); + shiftOut(DATA_PIN_595, CLOCK_PIN_595, MSBFIRST, shiftOutRegisters.bytes[1]); + shiftOut(DATA_PIN_595, CLOCK_PIN_595, MSBFIRST, shiftOutRegisters.bytes[0]); + digitalWrite(LATCH_PIN_595, HIGH); +} + + + + + diff --git a/DN23E08/coils.h b/DN23E08/coils.h new file mode 100644 index 0000000..be38459 --- /dev/null +++ b/DN23E08/coils.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +#define COIL_COUNT 40 + +#define DATA_PIN_595 9 +#define OE_PIN_595 10 +#define LATCH_PIN_595 11 +#define CLOCK_PIN_595 12 + +struct U6 { + bool A: 1; + bool B: 1; + bool C: 1; + bool D: 1; + bool E: 1; + bool F: 1; + bool G: 1; + bool H: 1; +}; + +struct U8 { + bool G1: 1; + bool G2: 1; + bool G3: 1; + bool G4: 1; + unsigned NC: 4; +}; + +struct U5 { + bool J1: 1; + bool J2: 1; + bool J3: 1; + bool J4: 1; + bool J5: 1; + bool J6: 1; + bool J7: 1; + bool J8: 1; +}; + +struct ShiftOutRegisters { + U6 u6; + U8 u8; + U5 u5; +}; + +union ShiftOutRegistersUnion { + ShiftOutRegisters shiftOutRegisters; + uint8_t bytes[3]; +}; + +struct Coils { + U5 relays; + U6 digit0; + U6 digit1; + U6 digit2; + U6 digit3; +}; + +void setupCoils(ModbusSerial& mb); +void coilsTask(ModbusSerial& mb); \ No newline at end of file diff --git a/DN23E08/discrete_inputs.cpp b/DN23E08/discrete_inputs.cpp new file mode 100644 index 0000000..d81fd22 --- /dev/null +++ b/DN23E08/discrete_inputs.cpp @@ -0,0 +1,61 @@ +#include "discrete_inputs.h" + +ShiftInRegisterUnion shiftInRegister; +Keys keys; +DiscreteInputs discreteInputs; + +void setupDiscreteInputs(ModbusSerial& mb) { + pinMode(KEY_1_PIN, INPUT_PULLUP); + pinMode(KEY_2_PIN, INPUT_PULLUP); + pinMode(KEY_3_PIN, INPUT_PULLUP); + pinMode(KEY_4_PIN, INPUT_PULLUP); + + pinMode(LOAD_PIN_165, OUTPUT); + pinMode(CLOCK_PIN_165, OUTPUT); + pinMode(DATA_PIN_165, INPUT); + + for (size_t i = 0; i < DISCRETE_INPUT_COUNT; i++) + mb.addIsts(i); +} + +void discreteInputsTask(ModbusSerial& mb) { + keys.KEY_1 = !digitalRead(KEY_1_PIN); + keys.KEY_2 = !digitalRead(KEY_2_PIN); + keys.KEY_3 = !digitalRead(KEY_3_PIN); + keys.KEY_4 = !digitalRead(KEY_4_PIN); + + digitalWrite(CLOCK_PIN_165, HIGH); + digitalWrite(LOAD_PIN_165, LOW); + digitalWrite(LOAD_PIN_165, HIGH); + shiftInRegister.byte = shiftIn(DATA_PIN_165, CLOCK_PIN_165, MSBFIRST) ^ 0XFF; + + discreteInputs.digitalInputs = shiftInRegister.shiftInRegister; + discreteInputs.keys = keys; + + mb.setIsts(0, discreteInputs.digitalInputs.INPUT1); + mb.setIsts(1, discreteInputs.digitalInputs.INPUT2); + mb.setIsts(2, discreteInputs.digitalInputs.INPUT3); + mb.setIsts(3, discreteInputs.digitalInputs.INPUT4); + mb.setIsts(4, discreteInputs.digitalInputs.INPUT5); + mb.setIsts(5, discreteInputs.digitalInputs.INPUT6); + mb.setIsts(6, discreteInputs.digitalInputs.INPUT7); + mb.setIsts(7, discreteInputs.digitalInputs.INPUT8); + + mb.setIsts(8, discreteInputs.keys.KEY_1); + mb.setIsts(9, discreteInputs.keys.KEY_2); + mb.setIsts(10, discreteInputs.keys.KEY_3); + mb.setIsts(11, discreteInputs.keys.KEY_4); +} + + + + + + + + + + + + + diff --git a/DN23E08/discrete_inputs.h b/DN23E08/discrete_inputs.h new file mode 100644 index 0000000..205c772 --- /dev/null +++ b/DN23E08/discrete_inputs.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#define DISCRETE_INPUT_COUNT 12 + +#define DATA_PIN_165 2 +#define CLOCK_PIN_165 3 +#define LOAD_PIN_165 4 + +#define KEY_1_PIN 5 +#define KEY_2_PIN 6 +#define KEY_3_PIN 7 +#define KEY_4_PIN 8 + +struct U11 { + bool INPUT1: 1; + bool INPUT2: 1; + bool INPUT3: 1; + bool INPUT4: 1; + bool INPUT5: 1; + bool INPUT6: 1; + bool INPUT7: 1; + bool INPUT8: 1; +}; + +union ShiftInRegisterUnion { + U11 shiftInRegister; + uint8_t byte; +}; + +struct Keys { + bool KEY_1; + bool KEY_2; + bool KEY_3; + bool KEY_4; +}; + +struct DiscreteInputs { + U11 digitalInputs; + Keys keys; +}; + +void setupDiscreteInputs(ModbusSerial& mb); +void discreteInputsTask(ModbusSerial& mb); \ No newline at end of file diff --git a/DN23E08/input_registers.cpp b/DN23E08/input_registers.cpp new file mode 100644 index 0000000..e9bfae6 --- /dev/null +++ b/DN23E08/input_registers.cpp @@ -0,0 +1,50 @@ +#include "input_registers.h" + +AnalogInputs analogInputs; + +void setupInputRegisters(ModbusSerial& mb) { + pinMode(ADC_0_PIN, INPUT); + pinMode(ADC_1_PIN, INPUT); + pinMode(ADC_2_PIN, INPUT); + pinMode(ADC_3_PIN, INPUT); + pinMode(ADC_4_PIN, INPUT); + pinMode(ADC_5_PIN, INPUT); + pinMode(ADC_6_PIN, INPUT); + pinMode(ADC_7_PIN, INPUT); + + for (size_t i = 0; i < INPUT_REGISTERS_COUNT; i++) + mb.addIreg(i); +} + +void inputRegistersTask(ModbusSerial& mb) { + analogInputs.ADC_0 = analogRead(ADC_0_PIN); + analogInputs.ADC_1 = analogRead(ADC_1_PIN); + analogInputs.ADC_2 = analogRead(ADC_2_PIN); + analogInputs.ADC_3 = analogRead(ADC_3_PIN); + analogInputs.ADC_4 = analogRead(ADC_4_PIN); + analogInputs.ADC_5 = analogRead(ADC_5_PIN); + analogInputs.ADC_6 = analogRead(ADC_6_PIN); + analogInputs.ADC_7 = analogRead(ADC_7_PIN); + + mb.setIreg(0, analogInputs.ADC_0); + mb.setIreg(1, analogInputs.ADC_1); + mb.setIreg(2, analogInputs.ADC_2); + mb.setIreg(3, analogInputs.ADC_3); + mb.setIreg(4, analogInputs.ADC_4); + mb.setIreg(5, analogInputs.ADC_5); + mb.setIreg(6, analogInputs.ADC_6); + mb.setIreg(7, analogInputs.ADC_7); +} + + + + + + + + + + + + + diff --git a/DN23E08/input_registers.h b/DN23E08/input_registers.h new file mode 100644 index 0000000..ec925e7 --- /dev/null +++ b/DN23E08/input_registers.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#define INPUT_REGISTERS_COUNT 8 + +#define ADC_0_PIN A0 +#define ADC_1_PIN A1 +#define ADC_2_PIN A2 +#define ADC_3_PIN A3 +#define ADC_4_PIN A4 +#define ADC_5_PIN A5 +#define ADC_6_PIN A6 +#define ADC_7_PIN A7 + +struct AnalogInputs { + uint16_t ADC_0; + uint16_t ADC_1; + uint16_t ADC_2; + uint16_t ADC_3; + uint16_t ADC_4; + uint16_t ADC_5; + uint16_t ADC_6; + uint16_t ADC_7; +}; + +void setupInputRegisters(ModbusSerial& mb); +void inputRegistersTask(ModbusSerial& mb); \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py index c7a0f3d..9eb93ae 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,4 +1,5 @@ +from .chaser import Chaser from .hardware import Hardware from .logic import loop -from .buttons_timers_and_relays import ButtonsTimersAndRelays +from .timed_relays import TimedRelays from .util import parse_commandline_args, connect_modbus_client \ No newline at end of file diff --git a/src/chaser.py b/src/chaser.py new file mode 100644 index 0000000..e412990 --- /dev/null +++ b/src/chaser.py @@ -0,0 +1,65 @@ +from enum import Enum +import time +from src.hardware import Hardware + +class Chaser: + """ + Manages the state machine for the chaser. + """ + + class State(Enum): + """ + Possible states + Make sure each state has a unique value! + """ + IDLE = 0 + CHASING = 1 + + def __init__(self): + """Initialize to state IDLE.""" + self.current_state = self.State.IDLE + self.current_led = 0 + self.chaser_sequence = [ + 0, # First display top segment + 8, # Second display top segment + 16, # Third display top segment + 24, 25, 26, 27, # Fourth display + 19, # Third display bottom segment + 11, # Second display bottom segment + 3, 4, 5 # First display remaining segments + ] + self.last_update_time = time.time() + + def update_state_machine(self, hardware: Hardware) -> None: + """ + Execute one iteration of the state machine loop. + + Args: + hardware (Hardware): The hardware interface object. + """ + next_state = self.current_state + + # State transitions + match self.current_state: + case self.State.IDLE: + if hardware.get_button_3(): + next_state = self.State.CHASING + self.current_led = 0 + case self.State.CHASING: + if hardware.get_button_2(): + next_state = self.State.IDLE + elif hardware.get_button_3(): + next_state = self.State.CHASING + self.current_led = 0 + elif time.time() - self.last_update_time > 0.1: # 100ms delay + self.current_led = (self.current_led + 1) % len(self.chaser_sequence) + self.last_update_time = time.time() + + self.current_state = next_state + + # Outputs + for i in range(32): + hardware.set_led(i, False) + + if self.current_state == self.State.CHASING: + hardware.set_led(self.chaser_sequence[self.current_led], True) diff --git a/src/hardware.py b/src/hardware.py index 52c4e08..9312819 100644 --- a/src/hardware.py +++ b/src/hardware.py @@ -2,12 +2,12 @@ from pymodbus.client import ModbusSerialClient class Hardware: """ - Represents the hardware interface for the Arduino Python Modbus Example. + Represents the hardware interface and syncs with the Modbus device. """ def __init__(self, client: ModbusSerialClient, slave_id: int): """ - Initialize the Hardware object. + Initialize to default values. Args: client (ModbusSerialClient): The Modbus client for communication. @@ -17,8 +17,13 @@ class Hardware: self.slave_id = slave_id self.relay_0 = False self.relay_1 = False + + self.leds = [False for i in range(32)] + self.button_0 = False self.button_1 = False + self.button_2 = False + self.button_3 = False def sync_hardware_state(self) -> None: """ @@ -26,10 +31,13 @@ class Hardware: """ try: self.client.write_coils(0, [self.relay_0, self.relay_1], slave=self.slave_id) - input_response = self.client.read_discrete_inputs(0, 2, slave=self.slave_id) + self.client.write_coils(8, self.leds, slave=self.slave_id) + input_response = self.client.read_discrete_inputs(8, 4, slave=self.slave_id) if not input_response.isError(): self.button_0 = input_response.bits[0] self.button_1 = input_response.bits[1] + self.button_2 = input_response.bits[2] + self.button_3 = input_response.bits[3] except Exception as e: print(f"Modbus error: {e}") @@ -41,10 +49,22 @@ class Hardware: """Set the state of relay 1.""" self.relay_1 = value + def set_led(self, index: int, value: bool) -> None: + """Set the state of an LED.""" + self.leds[index] = value + def get_button_0(self) -> bool: """Get the state of button 0.""" return self.button_0 def get_button_1(self) -> bool: """Get the state of button 1.""" - return self.button_1 \ No newline at end of file + return self.button_1 + + def get_button_2(self) -> bool: + """Get the state of button 2.""" + return self.button_2 + + def get_button_3(self) -> bool: + """Get the state of button 3.""" + return self.button_3 \ No newline at end of file diff --git a/src/logic.py b/src/logic.py index ef9bbb4..b63eb56 100644 --- a/src/logic.py +++ b/src/logic.py @@ -1,13 +1,17 @@ -from src.buttons_timers_and_relays import ButtonsTimersAndRelays +from src.chaser import Chaser +from src.timed_relays import TimedRelays from src.hardware import Hardware -buttons_timers_relays = ButtonsTimersAndRelays() +chaser = Chaser() +timed_relays = TimedRelays() def loop(hardware: Hardware) -> None: """ - Main logic loop for the Arduino Python Modbus Example. + One iteration of the main logic loop. + Calls both state machines. Args: hardware (Hardware): The hardware interface object. """ - buttons_timers_relays.update_state_machine(hardware) \ No newline at end of file + chaser.update_state_machine(hardware) + timed_relays.update_state_machine(hardware) diff --git a/src/buttons_timers_and_relays.py b/src/timed_relays.py similarity index 93% rename from src/buttons_timers_and_relays.py rename to src/timed_relays.py index 1adbb77..0ba21b9 100644 --- a/src/buttons_timers_and_relays.py +++ b/src/timed_relays.py @@ -2,13 +2,16 @@ from enum import Enum import time from src.hardware import Hardware -class ButtonsTimersAndRelays: +class TimedRelays: """ - Manages the state machine for buttons, timers, and relays. + Manages the state machine for the relays controlled by the first 2 buttons. """ class State(Enum): - """Enumeration of possible states for the state machine.""" + """ + Possible states + Make sure each state has a unique value! + """ RELAY_0_LOW = 0 RELAY_0_HIGH = 1 BUTTON_1_PRESSED = 2 @@ -17,7 +20,7 @@ class ButtonsTimersAndRelays: RELAY_1_HIGH_2 = 5 def __init__(self): - """Initialize the ButtonsTimersAndRelays object.""" + """Initialize to state RELAY_0_LOW.""" self.current_state = self.State.RELAY_0_LOW self.state_change_time = time.time() self.time_in_current_state = 0 diff --git a/src/util.py b/src/util.py index fe6d4ce..9ca34bc 100644 --- a/src/util.py +++ b/src/util.py @@ -21,7 +21,7 @@ def parse_commandline_args() -> argparse.Namespace: Returns: argparse.Namespace: Parsed command line arguments. """ - parser = argparse.ArgumentParser(description='Arduino Python Modbus Example') + parser = argparse.ArgumentParser(description='DN23E08 Python Modbus Example') parser.add_argument('--com_port', type=str, default=get_default_com_port(), help='COM port for the Modbus connection') parser.add_argument('--slave_id', type=int, default=1, diff --git a/uno_relay_modbus.ino b/uno_relay_modbus.ino deleted file mode 100644 index 2b2d813..0000000 --- a/uno_relay_modbus.ino +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#define BAUD_RATE 57600 -#define SLAVE_ID 1 -#define COIL_COUNT 4 -#define COIL_START 1 - -#define COIL_0 4 -#define COIL_1 5 -#define COIL_2 6 -#define COIL_3 7 - -#define STATUS_0 12 -#define STATUS_1 11 - -ModbusSerial mb (Serial, SLAVE_ID); - -void setup() { - Serial.begin (BAUD_RATE); - while (! Serial); - mb.config(BAUD_RATE); - - mb.addCoil (0); - mb.addCoil (1); - mb.addCoil (2); - mb.addCoil (3); - - mb.addIsts(0); - mb.addIsts(1); - - pinMode(COIL_0, OUTPUT); - pinMode(COIL_1, OUTPUT); - pinMode(COIL_2, OUTPUT); - pinMode(COIL_3, OUTPUT); - - pinMode(STATUS_0, INPUT_PULLUP); - pinMode(STATUS_1, INPUT_PULLUP); -} - -void loop() { - mb.task(); - readCoilValues(); - writeStatusValues(); -} - -void readCoilValues() { - digitalWrite(COIL_0, mb.coil(0)); - digitalWrite(COIL_1, mb.coil(1)); - digitalWrite(COIL_2, mb.coil(2)); - digitalWrite(COIL_3, mb.coil(3)); -} - -void writeStatusValues() { - mb.setIsts(0, !digitalRead(STATUS_0)); - mb.setIsts(1, !digitalRead(STATUS_1)); -} - - - - - - - - - - - - - -