diff --git a/.gitignore b/.gitignore index 2c6eb3893..c5b2fd429 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /.metadata /robots/.settings /robots/bin -eclipse.bat \ No newline at end of file +eclipse.bat +/out/ +/.idea/ diff --git a/robots/src/config/FileSupplier.java b/robots/src/config/FileSupplier.java new file mode 100644 index 000000000..ee90729cc --- /dev/null +++ b/robots/src/config/FileSupplier.java @@ -0,0 +1,7 @@ +package config; + +import java.io.File; + +public interface FileSupplier { + File getFile(); +} diff --git a/robots/src/config/PredeterminedPathFileSupplier.java b/robots/src/config/PredeterminedPathFileSupplier.java new file mode 100644 index 000000000..320a5e33c --- /dev/null +++ b/robots/src/config/PredeterminedPathFileSupplier.java @@ -0,0 +1,16 @@ +package config; + +import java.io.File; + +public class PredeterminedPathFileSupplier implements FileSupplier{ + private File path; + + public PredeterminedPathFileSupplier(String path) { + this.path = new File(path); + } + + @Override + public File getFile() { + return path; + } +} diff --git a/robots/src/controllers/RobotUpdateController.java b/robots/src/controllers/RobotUpdateController.java new file mode 100644 index 000000000..055389aba --- /dev/null +++ b/robots/src/controllers/RobotUpdateController.java @@ -0,0 +1,29 @@ +package controllers; + +import models.RobotModel; + +import java.util.Timer; +import java.util.TimerTask; + +public class RobotUpdateController { + + private final Timer m_timer = initTimer(); + private RobotModel m_model; + private static Timer initTimer() { + Timer timer = new Timer("events generator", true); + return timer; + } + public RobotUpdateController(RobotModel model){ + m_model=model; + m_timer.schedule(new TimerTask() { + @Override + public void run() { + updateModel(); + } + }, 0, 10); + } + + public void updateModel(){ + m_model.updatePos(); + } +} diff --git a/robots/src/controllers/TargetPositionController.java b/robots/src/controllers/TargetPositionController.java new file mode 100644 index 000000000..c36d945c4 --- /dev/null +++ b/robots/src/controllers/TargetPositionController.java @@ -0,0 +1,17 @@ +package controllers; + +import models.TargetModel; + +import java.awt.*; + +public class TargetPositionController { + private TargetModel m_model; + + + public TargetPositionController(TargetModel model){ + m_model=model; + } + public void setTargetPos(Point p){ + m_model.setPos(p.x,p.y); + } +} diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java index f82cfd8f8..d72303396 100644 --- a/robots/src/gui/GameVisualizer.java +++ b/robots/src/gui/GameVisualizer.java @@ -1,61 +1,43 @@ package gui; -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; +import controllers.TargetPositionController; +import gui.drawModels.RobotRepresentation; +import gui.drawModels.TargetRepresentation; + +import models.RobotModel; +import models.TargetModel; + +import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.geom.AffineTransform; -import java.util.Timer; -import java.util.TimerTask; +import java.util.Observable; +import java.util.Observer; import javax.swing.JPanel; -public class GameVisualizer extends JPanel -{ - private final Timer m_timer = initTimer(); - - private static Timer initTimer() - { - Timer timer = new Timer("events generator", true); - return timer; - } - - private volatile double m_robotPositionX = 100; - private volatile double m_robotPositionY = 100; - private volatile double m_robotDirection = 0; - - private volatile int m_targetPositionX = 150; - private volatile int m_targetPositionY = 100; - - private static final double maxVelocity = 0.1; - private static final double maxAngularVelocity = 0.001; - - public GameVisualizer() - { - m_timer.schedule(new TimerTask() - { - @Override - public void run() - { - onRedrawEvent(); - } - }, 0, 50); - m_timer.schedule(new TimerTask() - { - @Override - public void run() - { - onModelUpdateEvent(); - } - }, 0, 10); - addMouseListener(new MouseAdapter() - { +public class GameVisualizer extends JPanel implements Observer{ + + + + private RobotRepresentation m_robotView; + private TargetRepresentation m_targetView; + + private TargetPositionController m_targetController; + private RobotModel m_robot; + + + + + public GameVisualizer(TargetPositionController modelController, RobotModel model, TargetModel target) { + + m_robot =model; + m_robotView = new RobotRepresentation(m_robot); + m_targetView = new TargetRepresentation(target); + m_targetController = modelController; + + addMouseListener(new MouseAdapter() { @Override - public void mouseClicked(MouseEvent e) - { + public void mouseClicked(MouseEvent e) { setTargetPosition(e.getPoint()); repaint(); } @@ -63,148 +45,37 @@ public void mouseClicked(MouseEvent e) setDoubleBuffered(true); } - protected void setTargetPosition(Point p) - { - m_targetPositionX = p.x; - m_targetPositionY = p.y; + protected void setTargetPosition(Point p) { + + + m_targetController.setTargetPos(p); } - - protected void onRedrawEvent() - { + + protected void onRedrawEvent() { EventQueue.invokeLater(this::repaint); } - private static double distance(double x1, double y1, double x2, double y2) - { - double diffX = x1 - x2; - double diffY = y1 - y2; - return Math.sqrt(diffX * diffX + diffY * diffY); - } - - private static double angleTo(double fromX, double fromY, double toX, double toY) - { - double diffX = toX - fromX; - double diffY = toY - fromY; - - return asNormalizedRadians(Math.atan2(diffY, diffX)); - } - - protected void onModelUpdateEvent() - { - double distance = distance(m_targetPositionX, m_targetPositionY, - m_robotPositionX, m_robotPositionY); - if (distance < 0.5) - { - return; - } - double velocity = maxVelocity; - double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY); - double angularVelocity = 0; - if (angleToTarget > m_robotDirection) - { - angularVelocity = maxAngularVelocity; - } - if (angleToTarget < m_robotDirection) - { - angularVelocity = -maxAngularVelocity; - } - - moveRobot(velocity, angularVelocity, 10); - } - - private static double applyLimits(double value, double min, double max) - { - if (value < min) - return min; - if (value > max) - return max; - return value; - } - - private void moveRobot(double velocity, double angularVelocity, double duration) - { - velocity = applyLimits(velocity, 0, maxVelocity); - angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity); - double newX = m_robotPositionX + velocity / angularVelocity * - (Math.sin(m_robotDirection + angularVelocity * duration) - - Math.sin(m_robotDirection)); - if (!Double.isFinite(newX)) - { - newX = m_robotPositionX + velocity * duration * Math.cos(m_robotDirection); - } - double newY = m_robotPositionY - velocity / angularVelocity * - (Math.cos(m_robotDirection + angularVelocity * duration) - - Math.cos(m_robotDirection)); - if (!Double.isFinite(newY)) - { - newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection); - } - m_robotPositionX = newX; - m_robotPositionY = newY; - double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration); - m_robotDirection = newDirection; - } - private static double asNormalizedRadians(double angle) - { - while (angle < 0) - { - angle += 2*Math.PI; - } - while (angle >= 2*Math.PI) - { - angle -= 2*Math.PI; - } - return angle; - } - - private static int round(double value) - { - return (int)(value + 0.5); + protected void onModelUpdateEvent() { + onRedrawEvent(); } - + + @Override - public void paint(Graphics g) - { + public void paint(Graphics g) { super.paint(g); - Graphics2D g2d = (Graphics2D)g; - drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection); - drawTarget(g2d, m_targetPositionX, m_targetPositionY); - } - - private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) - { - g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); - } - - private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) - { - g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); - } - - private void drawRobot(Graphics2D g, int x, int y, double direction) - { - int robotCenterX = round(m_robotPositionX); - int robotCenterY = round(m_robotPositionY); - AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); - g.setTransform(t); - g.setColor(Color.MAGENTA); - fillOval(g, robotCenterX, robotCenterY, 30, 10); - g.setColor(Color.BLACK); - drawOval(g, robotCenterX, robotCenterY, 30, 10); - g.setColor(Color.WHITE); - fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); - g.setColor(Color.BLACK); - drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); + Graphics2D g2d = (Graphics2D) g; + m_robotView.draw(g2d); + m_targetView.draw(g2d); + } - - private void drawTarget(Graphics2D g, int x, int y) - { - AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); - g.setTransform(t); - g.setColor(Color.GREEN); - fillOval(g, x, y, 5, 5); - g.setColor(Color.BLACK); - drawOval(g, x, y, 5, 5); + + + @Override + public void update(Observable o, Object arg) { + EventQueue.invokeLater(this::onModelUpdateEvent); } + + + } diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java index ecb63c00f..eb77ea017 100644 --- a/robots/src/gui/GameWindow.java +++ b/robots/src/gui/GameWindow.java @@ -1,20 +1,36 @@ package gui; +import controllers.TargetPositionController; +import gui.serial.SerializableFrame; +import models.RobotModel; +import models.TargetModel; +import windowConstructors.GameWindowConstructor; +import windowConstructors.WindowConstructor; + + import java.awt.BorderLayout; import javax.swing.JInternalFrame; import javax.swing.JPanel; -public class GameWindow extends JInternalFrame +public class GameWindow extends JInternalFrame implements SerializableFrame { private final GameVisualizer m_visualizer; - public GameWindow() + public GameWindow(RobotModel model, TargetPositionController controller, TargetModel target) { super("Игровое поле", true, true, true, true); - m_visualizer = new GameVisualizer(); + + m_visualizer = new GameVisualizer(controller,model,target); + model.addObserver(m_visualizer); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_visualizer, BorderLayout.CENTER); getContentPane().add(panel); pack(); } + + + @Override + public WindowConstructor getFrameState() { + return new GameWindowConstructor(getX(),getY(),getSize().width,getSize().height); + } } diff --git a/robots/src/gui/LogWindow.java b/robots/src/gui/LogWindow.java index 723d3e2fc..7e53ce46e 100644 --- a/robots/src/gui/LogWindow.java +++ b/robots/src/gui/LogWindow.java @@ -7,11 +7,15 @@ import javax.swing.JInternalFrame; import javax.swing.JPanel; +import gui.serial.InnerWindowStateContainer; +import gui.serial.SerializableFrame; import log.LogChangeListener; import log.LogEntry; import log.LogWindowSource; +import windowConstructors.LogWindowConstructor; +import windowConstructors.WindowConstructor; -public class LogWindow extends JInternalFrame implements LogChangeListener +public class LogWindow extends JInternalFrame implements LogChangeListener, SerializableFrame { private LogWindowSource m_logSource; private TextArea m_logContent; @@ -33,6 +37,7 @@ public LogWindow(LogWindowSource logSource) private void updateLogContent() { + StringBuilder content = new StringBuilder(); for (LogEntry entry : m_logSource.all()) { @@ -47,4 +52,9 @@ public void onLogChanged() { EventQueue.invokeLater(this::updateLogContent); } + + @Override + public WindowConstructor getFrameState() { + return new LogWindowConstructor(getX(),getY(),getWidth(),getHeight()); + } } diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java index 62e943ee1..3b980a1fe 100644 --- a/robots/src/gui/MainApplicationFrame.java +++ b/robots/src/gui/MainApplicationFrame.java @@ -3,6 +3,8 @@ import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import javax.swing.JDesktopPane; import javax.swing.JFrame; @@ -16,6 +18,7 @@ import log.Logger; + /** * Что требуется сделать: * 1. Метод создания меню перегружен функционалом и трудно читается. @@ -25,75 +28,38 @@ public class MainApplicationFrame extends JFrame { private final JDesktopPane desktopPane = new JDesktopPane(); - - public MainApplicationFrame() { - //Make the big window be indented 50 pixels from each edge - //of the screen. - int inset = 50; + + + + + public MainApplicationFrame(){ + int inset = 50; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setBounds(inset, inset, - screenSize.width - inset*2, - screenSize.height - inset*2); + screenSize.width - inset*2, + screenSize.height - inset*2); setContentPane(desktopPane); - - - LogWindow logWindow = createLogWindow(); - addWindow(logWindow); - GameWindow gameWindow = new GameWindow(); - gameWindow.setSize(400, 400); - addWindow(gameWindow); + + setJMenuBar(generateMenuBar()); setDefaultCloseOperation(EXIT_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + super.windowClosing(e); + } + }); } - protected LogWindow createLogWindow() - { - LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource()); - logWindow.setLocation(10,10); - logWindow.setSize(300, 800); - setMinimumSize(logWindow.getSize()); - logWindow.pack(); - Logger.debug("Протокол работает"); - return logWindow; - } - - protected void addWindow(JInternalFrame frame) + public void addWindow(JInternalFrame frame) { desktopPane.add(frame); frame.setVisible(true); } - -// protected JMenuBar createMenuBar() { -// JMenuBar menuBar = new JMenuBar(); -// -// //Set up the lone menu. -// JMenu menu = new JMenu("Document"); -// menu.setMnemonic(KeyEvent.VK_D); -// menuBar.add(menu); -// -// //Set up the first menu item. -// JMenuItem menuItem = new JMenuItem("New"); -// menuItem.setMnemonic(KeyEvent.VK_N); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_N, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("new"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// //Set up the second menu item. -// menuItem = new JMenuItem("Quit"); -// menuItem.setMnemonic(KeyEvent.VK_Q); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_Q, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("quit"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// return menuBar; -// } + private JMenuBar generateMenuBar() { @@ -139,7 +105,9 @@ private JMenuBar generateMenuBar() menuBar.add(testMenu); return menuBar; } - + public JInternalFrame[] getInternalFrames(){ + return desktopPane.getAllFrames(); + } private void setLookAndFeel(String className) { try @@ -153,4 +121,5 @@ private void setLookAndFeel(String className) // just ignore } } + } diff --git a/robots/src/gui/PositionShowWindow.java b/robots/src/gui/PositionShowWindow.java new file mode 100644 index 000000000..fd69895ee --- /dev/null +++ b/robots/src/gui/PositionShowWindow.java @@ -0,0 +1,48 @@ +package gui; + + +import gui.serial.InnerWindowStateContainer; +import gui.serial.SerializableFrame; +import models.RobotModel; +import models.states.RobotStateReader; +import windowConstructors.PositionShowConstructor; +import windowConstructors.WindowConstructor; + +import javax.swing.*; +import java.awt.*; +import java.util.Observable; +import java.util.Observer; + +public class PositionShowWindow extends JInternalFrame implements Observer, SerializableFrame { + private RobotModel m_model; + private JLabel m_labelX; + private JLabel m_labelY; + public PositionShowWindow(RobotModel model){ + super("Robot coordinates", true, true, true, true); + m_model=model; + RobotStateReader state= m_model.getState(); + m_labelX=new JLabel("X : %f".formatted(state.getX())); + m_labelY=new JLabel("T : %f".formatted(state.getY())); + m_model.addObserver(this); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(m_labelX, BorderLayout.CENTER); + panel.add(m_labelY, BorderLayout.AFTER_LAST_LINE); + getContentPane().add(panel); + pack(); + } + + @Override + public void update(Observable o, Object arg) { + EventQueue.invokeLater(this::onTextUpdate); + } + void onTextUpdate(){ + RobotStateReader state= m_model.getState(); + m_labelX.setText("X : %f".formatted(state.getX())); + m_labelY.setText("Y : %f".formatted(state.getY())); + } + + @Override + public WindowConstructor getFrameState() { + return new PositionShowConstructor(getX(),getY(),getSize().width,getSize().height); + } +} diff --git a/robots/src/gui/RobotsProgram.java b/robots/src/gui/RobotsProgram.java index ae0930a8b..856dab6c9 100644 --- a/robots/src/gui/RobotsProgram.java +++ b/robots/src/gui/RobotsProgram.java @@ -1,25 +1,124 @@ package gui; -import java.awt.Frame; +import config.FileSupplier; +import config.PredeterminedPathFileSupplier; +import controllers.RobotUpdateController; +import gui.serial.InnerWindowStateContainer; +import gui.serial.SerializableFrame; +import serviceLocators.ModelAndControllerLocator; +import windowConstructors.*; + +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.*; +import java.util.ArrayList; + +import javax.swing.*; + +public class RobotsProgram { + private static FileSupplier m_modelsPath = new PredeterminedPathFileSupplier(System.getProperty("user.home").concat("\\modelsConfig.conf")); + private static FileSupplier m_windowsPath = new PredeterminedPathFileSupplier(System.getProperty("user.home").concat("\\windowsConfig.conf")); + private static MainApplicationFrame m_frame; + public static ModelAndControllerLocator m_locator; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -public class RobotsProgram -{ public static void main(String[] args) { - try { - UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); + try { + UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); // UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); - } catch (Exception e) { - e.printStackTrace(); - } - SwingUtilities.invokeLater(() -> { - MainApplicationFrame frame = new MainApplicationFrame(); - frame.pack(); - frame.setVisible(true); - frame.setExtendedState(Frame.MAXIMIZED_BOTH); - }); - }} + } catch (Exception e) { + e.printStackTrace(); + } + + SwingUtilities.invokeLater(() -> { + try (var modelsInputStream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(m_modelsPath.getFile().getPath()))); + var windowsInputStream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(m_windowsPath.getFile().getPath())))) { + readProgramState(modelsInputStream, windowsInputStream); + } catch (IOException | ClassNotFoundException e) { + initProgramState(); + } + + m_frame.pack(); + m_frame.setVisible(true); + m_frame.setExtendedState(Frame.MAXIMIZED_BOTH); + m_frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + try { + onExit(); + } catch (FileNotFoundException ex) { + throw new RuntimeException(ex); + } + + } + }); + + + }); + } + + private static void initProgramState() { + m_frame = new MainApplicationFrame(); + m_locator = ModelAndControllerLocator.getDefault(); + InnerWindowStateContainer defaultWindowLayout = new InnerWindowStateContainer(0, 0, 200, 200); + var updater = new RobotUpdateController(m_locator.getRobotModel()); + ArrayList frameConstructors = new ArrayList<>(); + frameConstructors.add(new GameWindowConstructor(defaultWindowLayout)); + frameConstructors.add(new LogWindowConstructor(defaultWindowLayout)); + frameConstructors.add(new PositionShowConstructor(defaultWindowLayout)); + + m_frame = new MainApplicationFrame(); + for (var constructor : frameConstructors) { + m_frame.addWindow(constructor.construct(m_locator)); + } + + + } + + private static void readProgramState(ObjectInputStream modelsReader, ObjectInputStream windowReader) throws IOException, ClassNotFoundException { + m_frame = new MainApplicationFrame(); + m_locator = ModelAndControllerLocator.getFromConfig(modelsReader); + var updater = new RobotUpdateController(m_locator.getRobotModel()); + int windowsCount = (Integer) windowReader.readObject(); + for (int i = 0; i < windowsCount; i++) { + m_frame.addWindow(((WindowConstructor) windowReader.readObject()).construct(m_locator)); + } + + } + + private static void writeProgramState(ObjectOutputStream modelsWriter, ObjectOutputStream windowsWriter) throws IOException { + m_locator.writeStateToConfig(modelsWriter); + JInternalFrame[] frames = m_frame.getInternalFrames(); + int frameCount = 0; + ArrayList constructors = new ArrayList<>(); + for (JInternalFrame frame : frames) { + if (frame instanceof SerializableFrame) { + frameCount++; + constructors.add(((SerializableFrame) frame).getFrameState()); + } + } + windowsWriter.writeObject(frameCount); + for (var constructor : constructors) { + windowsWriter.writeObject(constructor); + } + } + + private static void onExit() throws FileNotFoundException { + + try (var modelsInputStream = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(m_modelsPath.getFile().getPath()))); + var windowsInputStream = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(m_windowsPath.getFile().getPath()))) + ) { + + writeProgramState(modelsInputStream, windowsInputStream); + + } catch (IOException e) { + throw new RuntimeException(e); + + } + + + } +} diff --git a/robots/src/gui/drawModels/Drawable.java b/robots/src/gui/drawModels/Drawable.java new file mode 100644 index 000000000..ef9617464 --- /dev/null +++ b/robots/src/gui/drawModels/Drawable.java @@ -0,0 +1,9 @@ +package gui.drawModels; + +import java.awt.*; + +public interface Drawable { + void draw(Graphics2D g); + + +} diff --git a/robots/src/gui/drawModels/RobotRepresentation.java b/robots/src/gui/drawModels/RobotRepresentation.java new file mode 100644 index 000000000..b9866eac7 --- /dev/null +++ b/robots/src/gui/drawModels/RobotRepresentation.java @@ -0,0 +1,37 @@ +package gui.drawModels; +import models.RobotModel; +import models.states.RobotStateReader; + +import static utils.DrawUtils.*; +import static utils.MathUtils.*; +import java.awt.*; +import java.awt.geom.AffineTransform; + +public class RobotRepresentation implements Drawable{ + private RobotModel m_model; + + public RobotRepresentation(RobotModel model) { + m_model=model; + } + + + @Override + public void draw(Graphics2D g) { + RobotStateReader state=m_model.getState(); + int robotCenterX = round(state.getX()); + int robotCenterY = round(state.getY()); + AffineTransform t = AffineTransform.getRotateInstance(state.getDir(), robotCenterX, robotCenterY); + g.setTransform(t); + g.setColor(Color.MAGENTA); + fillOval(g, robotCenterX, robotCenterY, 30, 10); + g.setColor(Color.BLACK); + drawOval(g, robotCenterX, robotCenterY, 30, 10); + g.setColor(Color.WHITE); + fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); + g.setColor(Color.BLACK); + drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); + } + + + +} diff --git a/robots/src/gui/drawModels/TargetRepresentation.java b/robots/src/gui/drawModels/TargetRepresentation.java new file mode 100644 index 000000000..fc71f4d91 --- /dev/null +++ b/robots/src/gui/drawModels/TargetRepresentation.java @@ -0,0 +1,34 @@ +package gui.drawModels; + + +import models.TargetModel; +import models.states.TargetStateReader; + +import java.awt.*; +import java.awt.geom.AffineTransform; + +import static utils.DrawUtils.*; +import static utils.MathUtils.*; + +public class TargetRepresentation implements Drawable { + + private TargetModel m_model; + + public TargetRepresentation(TargetModel model) { + m_model=model; + } + + + @Override + public void draw(Graphics2D g) { + TargetStateReader targetCoord=m_model.getState(); + AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); + g.setTransform(t); + g.setColor(Color.GREEN); + fillOval(g, round(targetCoord.getX()) ,round(targetCoord.getY()) , 5, 5); + g.setColor(Color.BLACK); + drawOval(g, round(targetCoord.getX()) ,round(targetCoord.getY()), 5, 5); + } + + +} diff --git a/robots/src/gui/serial/InnerWindowStateContainer.java b/robots/src/gui/serial/InnerWindowStateContainer.java new file mode 100644 index 000000000..43f20aeb2 --- /dev/null +++ b/robots/src/gui/serial/InnerWindowStateContainer.java @@ -0,0 +1,35 @@ +package gui.serial; + +import javax.swing.*; +import java.io.Serializable; + +public class InnerWindowStateContainer implements Serializable { + + + public int x; + public int y; + public int sizeX; + public int sizeY; + + + + public InnerWindowStateContainer(int x, int y, int sizeX, int sizeY) { + this.x = x; + this.y = y; + this.sizeX = sizeX; + this.sizeY = sizeY; + + } + public void applyState(JInternalFrame frame){ + frame.setLocation(x,y); + frame.setSize(sizeX,sizeY); + } + + + + + + + + +} diff --git a/robots/src/gui/serial/SerializableFrame.java b/robots/src/gui/serial/SerializableFrame.java new file mode 100644 index 000000000..e5802695a --- /dev/null +++ b/robots/src/gui/serial/SerializableFrame.java @@ -0,0 +1,7 @@ +package gui.serial; + +import windowConstructors.WindowConstructor; + +public interface SerializableFrame { + WindowConstructor getFrameState(); +} diff --git a/robots/src/log/LogEntry.java b/robots/src/log/LogEntry.java index 3d9147107..f6665e9f5 100644 --- a/robots/src/log/LogEntry.java +++ b/robots/src/log/LogEntry.java @@ -1,6 +1,8 @@ package log; -public class LogEntry +import java.io.Serializable; + +public class LogEntry implements Serializable { private LogLevel m_logLevel; private String m_strMessage; diff --git a/robots/src/log/LogLevel.java b/robots/src/log/LogLevel.java index 582d010cc..4f06ff14d 100644 --- a/robots/src/log/LogLevel.java +++ b/robots/src/log/LogLevel.java @@ -1,6 +1,8 @@ package log; -public enum LogLevel +import java.io.Serializable; + +public enum LogLevel implements Serializable { Trace(0), Debug(1), diff --git a/robots/src/log/LogWindowSource.java b/robots/src/log/LogWindowSource.java index ca0ce4426..bff7c76dd 100644 --- a/robots/src/log/LogWindowSource.java +++ b/robots/src/log/LogWindowSource.java @@ -1,5 +1,7 @@ package log; +import log.states.LoggerSourceState; + import java.util.ArrayList; import java.util.Collections; @@ -26,6 +28,11 @@ public LogWindowSource(int iQueueLength) m_messages = new ArrayList(iQueueLength); m_listeners = new ArrayList(); } + public LogWindowSource(LoggerSourceState container){ + m_iQueueLength=container.qLength; + m_messages=container.logs; + m_listeners=new ArrayList(); + } public void registerListener(LogChangeListener listener) { @@ -86,4 +93,8 @@ public Iterable all() { return m_messages; } + + public LoggerSourceState getState(){ + return new LoggerSourceState(m_messages,m_iQueueLength); + } } diff --git a/robots/src/log/states/LoggerSourceState.java b/robots/src/log/states/LoggerSourceState.java new file mode 100644 index 000000000..d89cd576f --- /dev/null +++ b/robots/src/log/states/LoggerSourceState.java @@ -0,0 +1,15 @@ +package log.states; + +import log.LogEntry; + +import java.io.Serializable; +import java.util.ArrayList; + +public class LoggerSourceState implements Serializable { + public ArrayList logs; + public int qLength; + public LoggerSourceState(ArrayList l,int len){ + logs=l; + qLength=len; + } +} diff --git a/robots/src/models/RobotModel.java b/robots/src/models/RobotModel.java new file mode 100644 index 000000000..be5e62150 --- /dev/null +++ b/robots/src/models/RobotModel.java @@ -0,0 +1,119 @@ +package models; + + +import models.states.RobotState; +import models.states.RobotStateReader; +import models.states.TargetStateReader; + +import static utils.MathUtils.*; +import java.util.Observable; + +public class RobotModel extends Observable { + + + private volatile double m_PositionX = 100; + private volatile double m_PositionY = 100; + private TargetModel m_Target; + + private volatile double m_Direction = 0; + + + private double turn_duration=10; + + private static final double maxVelocity = 0.1; + private static final double maxAngularVelocity = 0.001; + + + public RobotModel(double m_PositionX, double m_PositionY, double m_Direction,TargetModel target) { + this.m_PositionX = m_PositionX; + this.m_PositionY = m_PositionY; + this.m_Direction = m_Direction; + m_Target=target; + } + public RobotModel(RobotState state,TargetModel target){ + this.m_Target=target; + this.m_PositionY=state.getY(); + this.m_PositionX=state.getX(); + this.m_Direction=state.getDir(); + } + + + public void updatePos(){ + if(isTooClose()){ + return; + } + double velocity=calculateVelocity(); + double angular = calculateAngularVelocity(); + move(velocity,angular); + setChanged(); + notifyObservers(); + } + + + + public RobotState getState(){ + RobotState ans; + synchronized (this){ + ans=new RobotState(m_PositionX,m_PositionY,m_Direction); + } + return ans; + + } + + + private double calculateVelocity(){ + return maxVelocity; + } + private double calculateAngularVelocity(){ + TargetStateReader targetCoord= m_Target.getState(); + double angleToTarget = angleTo(m_PositionX, m_PositionY, targetCoord.getX(), targetCoord.getY()); + double angularVelocity = 0; + if (angleToTarget > m_Direction) + { + angularVelocity = maxAngularVelocity; + } + if (angleToTarget < m_Direction) + { + angularVelocity = -maxAngularVelocity; + } + if(Math.abs(angleToTarget-m_Direction)>Math.PI){ + angularVelocity=-angularVelocity; + + } + return angularVelocity; + } + private boolean isTooClose(){ + TargetStateReader targetCoord= m_Target.getState(); + double distance = distance(targetCoord.getX(), targetCoord.getY(), + m_PositionX, m_PositionY); + return distance<0.5; + } + private void move(double velocity,double angularVelocity){ + velocity = applyLimits(velocity, 0, maxVelocity); + angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity); + double newX = m_PositionX + velocity / angularVelocity * + (Math.sin(m_Direction + angularVelocity * turn_duration) - + Math.sin(m_Direction)); + if (!Double.isFinite(newX)) + { + newX = m_PositionX + velocity * turn_duration * Math.cos(m_Direction); + } + double newY = m_PositionY - velocity / angularVelocity * + (Math.cos(m_Direction + angularVelocity * turn_duration) - + Math.cos(m_Direction)); + if (!Double.isFinite(newY)) + { + newY = m_PositionY + velocity * turn_duration * Math.sin(m_Direction); + } + m_PositionX = newX; + m_PositionY = newY; + double newDirection = asNormalizedRadians(m_Direction + angularVelocity * turn_duration); + m_Direction = newDirection; + } + + + + + + +} diff --git a/robots/src/models/TargetModel.java b/robots/src/models/TargetModel.java new file mode 100644 index 000000000..31327a3dd --- /dev/null +++ b/robots/src/models/TargetModel.java @@ -0,0 +1,35 @@ +package models; + + +import models.states.TargetState; +import models.states.TargetStateReader; + +import java.util.Observable; + +public class TargetModel extends Observable { + private volatile double m_PositionX = 100; + private volatile double m_PositionY = 100; + + public TargetModel(double posX, double posY) { + setPos(posX, posY); + } + public TargetModel(TargetState state){ + m_PositionX=state.getX(); + m_PositionY=state.getY(); + } + + public TargetState getState(){ + TargetState ans; + synchronized (this){ + ans=new TargetState(m_PositionX,m_PositionY); + } + return ans; + } + + public void setPos(double posX, double posY) { + m_PositionX = posX; + m_PositionY = posY; + setChanged(); + notifyObservers(); + } +} diff --git a/robots/src/models/states/RobotState.java b/robots/src/models/states/RobotState.java new file mode 100644 index 000000000..829694681 --- /dev/null +++ b/robots/src/models/states/RobotState.java @@ -0,0 +1,34 @@ +package models.states; + +import java.io.Serializable; + +public class RobotState implements RobotStateReader, Serializable { + + + private volatile double m_x; + private volatile double m_y; + private volatile double m_dir; + + public RobotState(double m_x, double m_y, double m_dir) { + this.m_x = m_x; + this.m_y = m_y; + this.m_dir = m_dir; + } + + @Override + public double getX() { + return m_x; + } + + @Override + public double getY() { + return m_y; + } + + @Override + public double getDir() { + return m_dir; + } + + +} diff --git a/robots/src/models/states/RobotStateReader.java b/robots/src/models/states/RobotStateReader.java new file mode 100644 index 000000000..543c6842b --- /dev/null +++ b/robots/src/models/states/RobotStateReader.java @@ -0,0 +1,7 @@ +package models.states; + +public interface RobotStateReader { + double getX(); + double getY(); + double getDir(); +} diff --git a/robots/src/models/states/TargetState.java b/robots/src/models/states/TargetState.java new file mode 100644 index 000000000..23490c0fa --- /dev/null +++ b/robots/src/models/states/TargetState.java @@ -0,0 +1,21 @@ +package models.states; + +import java.io.Serializable; + +public class TargetState implements TargetStateReader, Serializable { + private double x; + private double y; + + public TargetState(double x, double y) { + this.x = x; + this.y = y; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } +} diff --git a/robots/src/models/states/TargetStateReader.java b/robots/src/models/states/TargetStateReader.java new file mode 100644 index 000000000..d692a161b --- /dev/null +++ b/robots/src/models/states/TargetStateReader.java @@ -0,0 +1,6 @@ +package models.states; + +public interface TargetStateReader { + double getX(); + double getY(); +} diff --git a/robots/src/serviceLocators/ModelAndControllerLocator.java b/robots/src/serviceLocators/ModelAndControllerLocator.java new file mode 100644 index 000000000..e71455363 --- /dev/null +++ b/robots/src/serviceLocators/ModelAndControllerLocator.java @@ -0,0 +1,59 @@ +package serviceLocators; + +import controllers.TargetPositionController; +import log.LogWindowSource; +import log.Logger; +import log.states.LoggerSourceState; +import models.RobotModel; +import models.TargetModel; +import models.states.RobotState; +import models.states.TargetState; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class ModelAndControllerLocator { + private RobotModel m_robotModel; + private TargetModel m_targetModel; + private TargetPositionController m_targetController; + private LogWindowSource m_logs; + public ModelAndControllerLocator(RobotModel robotModel, TargetModel targetModel, TargetPositionController targetController, LogWindowSource m_logs) { + this.m_robotModel = robotModel; + this.m_targetModel = targetModel; + this.m_targetController = targetController; + this.m_logs = m_logs; + } + public static ModelAndControllerLocator getDefault(){ + var targetModel = new TargetModel(150, 100); + var robotModel = new RobotModel(100, 100, 100, targetModel); + var logs = Logger.getDefaultLogSource(); + var targetController = new TargetPositionController(targetModel); + return new ModelAndControllerLocator(robotModel, targetModel, targetController, logs); + } + public static ModelAndControllerLocator getFromConfig(ObjectInputStream reader) throws IOException, ClassNotFoundException { + RobotState robotState=(RobotState)reader.readObject(); + TargetState targetState=(TargetState)reader.readObject(); + TargetModel targetModel=new TargetModel(targetState); + RobotModel robotModel=new RobotModel(robotState,targetModel); + + var log=(LoggerSourceState)reader.readObject(); + LogWindowSource logs= Logger.getDefaultLogSource(); + for (var a: log.logs) { + logs.append(a.getLevel(),a.getMessage()); + } + var TargetController=new TargetPositionController(targetModel); +return new ModelAndControllerLocator(robotModel, targetModel, TargetController, logs); + } + public void writeStateToConfig(ObjectOutputStream writer) throws IOException { + writer.writeObject(m_robotModel.getState()); + writer.writeObject(m_targetModel.getState()); + writer.writeObject(m_logs.getState()); + } + public RobotModel getRobotModel(){return m_robotModel;}; + public TargetModel getTargetModel(){return m_targetModel;} + public TargetPositionController getTargetPositionController(){return m_targetController;} + public LogWindowSource getLogSource(){ + return m_logs; + } +} diff --git a/robots/src/utils/DrawUtils.java b/robots/src/utils/DrawUtils.java new file mode 100644 index 000000000..a87ef3489 --- /dev/null +++ b/robots/src/utils/DrawUtils.java @@ -0,0 +1,15 @@ +package utils; + +import java.awt.*; + +public class DrawUtils { + public static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) + { + g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); + } + + public static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) + { + g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); + } +} diff --git a/robots/src/utils/MathUtils.java b/robots/src/utils/MathUtils.java new file mode 100644 index 000000000..bd7a6626a --- /dev/null +++ b/robots/src/utils/MathUtils.java @@ -0,0 +1,45 @@ +package utils; + +public class MathUtils { + public static int round(double value) + { + return (int)(value + 0.5); + } + + public static double asNormalizedRadians(double angle) + { + while (angle < 0) + { + angle += 2*Math.PI; + } + while (angle >= 2*Math.PI) + { + angle -= 2*Math.PI; + } + return angle; + } + + public static double distance(double x1, double y1, double x2, double y2) + { + double diffX = x1 - x2; + double diffY = y1 - y2; + return Math.sqrt(diffX * diffX + diffY * diffY); + } + + public static double angleTo(double fromX, double fromY, double toX, double toY) + { + double diffX = toX - fromX; + double diffY = toY - fromY; + + return asNormalizedRadians(Math.atan2(diffY, diffX)); + } + public static double applyLimits(double value, double min, double max) + { + if (value < min) + return min; + if (value > max) + return max; + return value; + } + +} diff --git a/robots/src/windowConstructors/AbstractWindowConstructor.java b/robots/src/windowConstructors/AbstractWindowConstructor.java new file mode 100644 index 000000000..de9955301 --- /dev/null +++ b/robots/src/windowConstructors/AbstractWindowConstructor.java @@ -0,0 +1,25 @@ +package windowConstructors; + +import gui.serial.InnerWindowStateContainer; +import serviceLocators.ModelAndControllerLocator; + +import javax.swing.*; +import java.io.Serializable; + + +public abstract class AbstractWindowConstructor implements WindowConstructor, Serializable { + protected InnerWindowStateContainer m_windowState; + + protected AbstractWindowConstructor(InnerWindowStateContainer m_windowState) { + this.m_windowState = m_windowState; + } + public AbstractWindowConstructor(int x, int y,int width,int height){ + m_windowState=new InnerWindowStateContainer(x,y,width,height); + } + protected abstract JInternalFrame windowFabricMethod(ModelAndControllerLocator locator); + public JInternalFrame construct(ModelAndControllerLocator locator){ + var window= windowFabricMethod(locator); + m_windowState.applyState(window); + return window; + } +} diff --git a/robots/src/windowConstructors/GameWindowConstructor.java b/robots/src/windowConstructors/GameWindowConstructor.java new file mode 100644 index 000000000..edc11b881 --- /dev/null +++ b/robots/src/windowConstructors/GameWindowConstructor.java @@ -0,0 +1,21 @@ +package windowConstructors; + +import gui.GameWindow; +import gui.serial.InnerWindowStateContainer; +import serviceLocators.ModelAndControllerLocator; + +import javax.swing.*; + +public class GameWindowConstructor extends AbstractWindowConstructor{ + public GameWindowConstructor(InnerWindowStateContainer m_windowState) { + super(m_windowState); + } + public GameWindowConstructor(int x, int y, int width, int height){ + super(x,y,width,height); + m_windowState=new InnerWindowStateContainer(x,y,width,height); + } + @Override + protected JInternalFrame windowFabricMethod(ModelAndControllerLocator locator) { + return new GameWindow(locator.getRobotModel(),locator.getTargetPositionController(),locator.getTargetModel()); + } +} diff --git a/robots/src/windowConstructors/LogWindowConstructor.java b/robots/src/windowConstructors/LogWindowConstructor.java new file mode 100644 index 000000000..67094a3cc --- /dev/null +++ b/robots/src/windowConstructors/LogWindowConstructor.java @@ -0,0 +1,21 @@ +package windowConstructors; + +import gui.LogWindow; +import gui.serial.InnerWindowStateContainer; +import serviceLocators.ModelAndControllerLocator; + +import javax.swing.*; + +public class LogWindowConstructor extends AbstractWindowConstructor{ + public LogWindowConstructor(InnerWindowStateContainer m_windowState) { + super(m_windowState); + } + public LogWindowConstructor(int x, int y,int width,int height){ + super(x,y,width,height); + m_windowState=new InnerWindowStateContainer(x,y,width,height); + } + @Override + protected JInternalFrame windowFabricMethod(ModelAndControllerLocator locator) { + return new LogWindow(locator.getLogSource()); + } +} diff --git a/robots/src/windowConstructors/PositionShowConstructor.java b/robots/src/windowConstructors/PositionShowConstructor.java new file mode 100644 index 000000000..f47050c22 --- /dev/null +++ b/robots/src/windowConstructors/PositionShowConstructor.java @@ -0,0 +1,21 @@ +package windowConstructors; + +import gui.PositionShowWindow; +import gui.serial.InnerWindowStateContainer; +import serviceLocators.ModelAndControllerLocator; + +import javax.swing.*; + +public class PositionShowConstructor extends AbstractWindowConstructor{ + public PositionShowConstructor(InnerWindowStateContainer m_windowState) { + super(m_windowState); + } + public PositionShowConstructor(int x, int y,int width,int height){ + super(x,y,width,height); + + } + @Override + protected JInternalFrame windowFabricMethod(ModelAndControllerLocator locator) { + return new PositionShowWindow(locator.getRobotModel()); + } +} diff --git a/robots/src/windowConstructors/WindowConstructor.java b/robots/src/windowConstructors/WindowConstructor.java new file mode 100644 index 000000000..113977129 --- /dev/null +++ b/robots/src/windowConstructors/WindowConstructor.java @@ -0,0 +1,9 @@ +package windowConstructors; + +import serviceLocators.ModelAndControllerLocator; + +import javax.swing.*; + +public interface WindowConstructor { + JInternalFrame construct(ModelAndControllerLocator locator); +}