From 9c08f289564e11f4548e4c41a8b71596dbd96d71 Mon Sep 17 00:00:00 2001 From: Claes Jakobsson Date: Thu, 8 May 2014 22:29:10 +0200 Subject: [PATCH] Added EEPROM support for Cooja with a rudimentary hexdump -C like viewer in the mote interface view --- platform/cooja/Makefile.cooja | 3 +- platform/cooja/contiki-cooja-main.c | 6 +- platform/cooja/dev/eeprom.c | 99 +++++ tools/cooja/config/cooja_default.config | 2 +- .../contikimote/interfaces/ContikiEEPROM.java | 340 ++++++++++++++++++ 5 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 platform/cooja/dev/eeprom.c create mode 100644 tools/cooja/java/org/contikios/cooja/contikimote/interfaces/ContikiEEPROM.java diff --git a/platform/cooja/Makefile.cooja b/platform/cooja/Makefile.cooja index 1426bf7cc..047ca9d99 100644 --- a/platform/cooja/Makefile.cooja +++ b/platform/cooja/Makefile.cooja @@ -51,7 +51,8 @@ COOJA_BASE = simEnvChange.c cooja_mt.c cooja_mtarch.c rtimer-arch.c slip.c watch COOJA_INTFS = beep.c button-sensor.c ip.c leds-arch.c moteid.c \ pir-sensor.c rs232.c vib-sensor.c \ - clock.c log.c cfs-cooja.c cooja-radio.c + clock.c log.c cfs-cooja.c cooja-radio.c \ + eeprom.c COOJA_CORE = random.c sensors.c leds.c symbols.c diff --git a/platform/cooja/contiki-cooja-main.c b/platform/cooja/contiki-cooja-main.c index 035f9ee6b..3c8f98440 100644 --- a/platform/cooja/contiki-cooja-main.c +++ b/platform/cooja/contiki-cooja-main.c @@ -114,7 +114,8 @@ SIM_INTERFACE_NAME(pir_interface); SIM_INTERFACE_NAME(clock_interface); SIM_INTERFACE_NAME(leds_interface); SIM_INTERFACE_NAME(cfs_interface); -SIM_INTERFACES(&vib_interface, &moteid_interface, &rs232_interface, &simlog_interface, &beep_interface, &radio_interface, &button_interface, &pir_interface, &clock_interface, &leds_interface, &cfs_interface); +SIM_INTERFACE_NAME(eeprom_interface); +SIM_INTERFACES(&vib_interface, &moteid_interface, &rs232_interface, &simlog_interface, &beep_interface, &radio_interface, &button_interface, &pir_interface, &clock_interface, &leds_interface, &cfs_interface, &eeprom_interface); /* Example: manually add mote interfaces */ //SIM_INTERFACE_NAME(dummy_interface); //SIM_INTERFACES(..., &dummy_interface); @@ -320,6 +321,9 @@ contiki_init() } #endif /* WITH_UIP6 */ + /* Initialize eeprom */ + eeprom_init(); + /* Start serial process */ serial_line_init(); diff --git a/platform/cooja/dev/eeprom.c b/platform/cooja/dev/eeprom.c new file mode 100644 index 000000000..8a40fa3d8 --- /dev/null +++ b/platform/cooja/dev/eeprom.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "dev/eeprom.h" + +#include "lib/simEnvChange.h" + +const struct simInterface eeprom_interface; + +#define EEPROM_BUF_SIZE 1024 /* Configure EEPROM size here and in ContikiEeprom.java */ + +unsigned char simEEPROMData[EEPROM_BUF_SIZE]; +char simEEPROMChanged = 0; +int simEEPROMRead = 0; +int simEEPROMWritten = 0; + +void +eeprom_init(void) +{ +} + +void +eeprom_read(eeprom_addr_t addr, unsigned char *buf, int len) +{ + if (addr >= EEPROM_BUF_SIZE) { + return; + } + + if(addr + len >= EEPROM_BUF_SIZE) { + len = EEPROM_BUF_SIZE - addr; + } + + memcpy(buf, &simEEPROMData[addr], len); + + simEEPROMChanged = 1; + simEEPROMRead += len; +} + +void +eeprom_write(eeprom_addr_t addr, unsigned char *buf, int len) +{ + if (addr >= EEPROM_BUF_SIZE) { + return; + } + + if(addr + len >= EEPROM_BUF_SIZE) { + len = EEPROM_BUF_SIZE - addr; + } + + + memcpy(&simEEPROMData[addr], buf, len); + + simEEPROMChanged = 1; + simEEPROMWritten += len; + +} + +/*-----------------------------------------------------------------------------------*/ +static void +doInterfaceActionsBeforeTick(void) +{ +} +/*-----------------------------------------------------------------------------------*/ +static void +doInterfaceActionsAfterTick(void) +{ +} +/*-----------------------------------------------------------------------------------*/ + +SIM_INTERFACE(eeprom_interface, + doInterfaceActionsBeforeTick, + doInterfaceActionsAfterTick); diff --git a/tools/cooja/config/cooja_default.config b/tools/cooja/config/cooja_default.config index a48c89e2d..212f48003 100644 --- a/tools/cooja/config/cooja_default.config +++ b/tools/cooja/config/cooja_default.config @@ -1,6 +1,6 @@ org.contikios.cooja.contikimote.interfaces.ContikiRadio.RADIO_TRANSMISSION_RATE_kbps = 250 -org.contikios.cooja.contikimote.ContikiMoteType.MOTE_INTERFACES = org.contikios.cooja.interfaces.Position org.contikios.cooja.interfaces.Battery org.contikios.cooja.contikimote.interfaces.ContikiVib org.contikios.cooja.contikimote.interfaces.ContikiMoteID org.contikios.cooja.contikimote.interfaces.ContikiRS232 org.contikios.cooja.contikimote.interfaces.ContikiBeeper org.contikios.cooja.interfaces.RimeAddress org.contikios.cooja.contikimote.interfaces.ContikiIPAddress org.contikios.cooja.contikimote.interfaces.ContikiRadio org.contikios.cooja.contikimote.interfaces.ContikiButton org.contikios.cooja.contikimote.interfaces.ContikiPIR org.contikios.cooja.contikimote.interfaces.ContikiClock org.contikios.cooja.contikimote.interfaces.ContikiLED org.contikios.cooja.contikimote.interfaces.ContikiCFS org.contikios.cooja.interfaces.Mote2MoteRelations org.contikios.cooja.interfaces.MoteAttributes +org.contikios.cooja.contikimote.ContikiMoteType.MOTE_INTERFACES = org.contikios.cooja.interfaces.Position org.contikios.cooja.interfaces.Battery org.contikios.cooja.contikimote.interfaces.ContikiVib org.contikios.cooja.contikimote.interfaces.ContikiMoteID org.contikios.cooja.contikimote.interfaces.ContikiRS232 org.contikios.cooja.contikimote.interfaces.ContikiBeeper org.contikios.cooja.interfaces.RimeAddress org.contikios.cooja.contikimote.interfaces.ContikiIPAddress org.contikios.cooja.contikimote.interfaces.ContikiRadio org.contikios.cooja.contikimote.interfaces.ContikiButton org.contikios.cooja.contikimote.interfaces.ContikiPIR org.contikios.cooja.contikimote.interfaces.ContikiClock org.contikios.cooja.contikimote.interfaces.ContikiLED org.contikios.cooja.contikimote.interfaces.ContikiCFS org.contikios.cooja.contikimote.interfaces.ContikiEEPROM org.contikios.cooja.interfaces.Mote2MoteRelations org.contikios.cooja.interfaces.MoteAttributes org.contikios.cooja.contikimote.ContikiMoteType.C_SOURCES = org.contikios.cooja.Cooja.MOTETYPES = org.contikios.cooja.motes.ImportAppMoteType org.contikios.cooja.motes.DisturberMoteType org.contikios.cooja.contikimote.ContikiMoteType org.contikios.cooja.Cooja.PLUGINS = org.contikios.cooja.plugins.Visualizer org.contikios.cooja.plugins.LogListener org.contikios.cooja.plugins.TimeLine org.contikios.cooja.plugins.MoteInformation org.contikios.cooja.plugins.MoteInterfaceViewer org.contikios.cooja.plugins.VariableWatcher org.contikios.cooja.plugins.EventListener org.contikios.cooja.plugins.RadioLogger org.contikios.cooja.plugins.ScriptRunner org.contikios.cooja.plugins.Notes org.contikios.cooja.plugins.BufferListener org.contikios.cooja.plugins.DGRMConfigurator org.contikios.cooja.plugins.BaseRSSIconf diff --git a/tools/cooja/java/org/contikios/cooja/contikimote/interfaces/ContikiEEPROM.java b/tools/cooja/java/org/contikios/cooja/contikimote/interfaces/ContikiEEPROM.java new file mode 100644 index 000000000..a1b1d5e56 --- /dev/null +++ b/tools/cooja/java/org/contikios/cooja/contikimote/interfaces/ContikiEEPROM.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2014, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +package org.contikios.cooja.contikimote.interfaces; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.*; +import java.util.*; +import javax.swing.*; +import javax.xml.bind.DatatypeConverter; +import org.apache.log4j.Logger; +import org.jdom.Element; + +import org.contikios.cooja.*; +import org.contikios.cooja.contikimote.ContikiMoteInterface; +import org.contikios.cooja.interfaces.PolledAfterActiveTicks; + +/** + * Contiki EEPROM interface + * + * Contiki variables: + * + *

+ * + * Core interface: + *

+ *

+ * This observable notifies when the eeprom is used (read/write). + * + * @author Claes Jakobsson (based on ContikiCFS by Fredrik Osterlind) + */ +@ClassDescription("EEPROM") +public class ContikiEEPROM extends MoteInterface implements ContikiMoteInterface, PolledAfterActiveTicks { + private static Logger logger = Logger.getLogger(ContikiEEPROM.class); + + public int EEPROM_SIZE = 1024; /* Configure EEPROM size here and in eeprom.c. Should really be multiple of 16 */ + private Mote mote = null; + private SectionMoteMemory moteMem = null; + + private int lastRead = 0; + private int lastWritten = 0; + + /** + * Creates an interface to the EEPROM at mote. + * + * @param mote Mote + * @see Mote + * @see org.contikios.cooja.MoteInterfaceHandler + */ + public ContikiEEPROM(Mote mote) { + this.mote = mote; + this.moteMem = (SectionMoteMemory) mote.getMemory(); + } + + public static String[] getCoreInterfaceDependencies() { + return new String[]{"eeprom_interface"}; + } + + public void doActionsAfterTick() { + if (moteMem.getByteValueOf("simEEPROMChanged") == 1) { + lastRead = moteMem.getIntValueOf("simEEPROMRead"); + lastWritten = moteMem.getIntValueOf("simEEPROMWritten"); + + moteMem.setIntValueOf("simEEPROMRead", 0); + moteMem.setIntValueOf("simEEPROMWritten", 0); + moteMem.setByteValueOf("simEEPROMChanged", (byte) 0); + + this.setChanged(); + this.notifyObservers(mote); + } + } + + /** + * Set EEPROM data. + * + * @param data Data + * @return True if operation successful + */ + public boolean setEEPROMData(byte[] data) { + if (data.length > EEPROM_SIZE) { + logger.fatal("Error. EEPROM data too large, skipping"); + return false; + } + + moteMem.setByteArray("simEEPROMData", data); + return true; + } + + /** + * Get EEPROM data. + * + * @return Filesystem data + */ + public byte[] getEEPROMData() { + return moteMem.getByteArray("simEEPROMData", EEPROM_SIZE); + } + + /** + * @return Read bytes count last change. + */ + public int getLastReadCount() { + return lastRead; + } + + /** + * @return Written bytes count last change. + */ + public int getLastWrittenCount() { + return lastWritten; + } + + String byteArrayToPrintableCharacters(byte[] data, int offset, int length) { + StringBuilder sb = new StringBuilder(); + for (int i = offset; i < offset + length; i++) { + sb.append(data[i] > 31 && data[i] < 128 ? (char) data[i] : '.'); + } + return sb.toString(); + } + + String byteArrayToHexList(byte[] data, int offset, int length) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < length; i++) { + byte h = (byte) ((int) data[offset + i] >> 4); + byte l = (byte) ((int) data[offset + i] & 0xf); + sb.append((char)(h < 10 ? 0x30 + h : 0x61 + h - 10)); + sb.append((char)(l < 10 ? 0x30 + l : 0x61 + l - 10)); + sb.append(' '); + if (i % 8 == 7 && i != length - 1) { + sb.append(' '); + } + } + + return sb.toString(); + } + + void redrawDataView(JTextArea textArea) { + StringBuilder sb = new StringBuilder(); + Formatter fmt = new Formatter(sb); + byte[] data = getEEPROMData(); + + for (int i = 0; i < EEPROM_SIZE; i+= 16) { + fmt.format("%04d %s | %s |\n", i, byteArrayToHexList(data, i, 16), byteArrayToPrintableCharacters(data, i, 16)); + } + + textArea.setText(sb.toString()); + textArea.setCaretPosition(0); + } + + public JPanel getInterfaceVisualizer() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + + final JLabel lastTimeLabel = new JLabel("Last change at: ?"); + final JLabel lastReadLabel = new JLabel("Last change read bytes: 0"); + final JLabel lastWrittenLabel = new JLabel("Last change wrote bytes: 0"); + final JButton uploadButton = new JButton("Upload binary file"); + final JButton clearButton = new JButton("Reset EEPROM to zero"); + final JTextArea dataViewArea = new JTextArea(); + final JScrollPane dataViewScrollPane = new JScrollPane(dataViewArea); + + panel.add(lastTimeLabel); + panel.add(lastReadLabel); + panel.add(lastWrittenLabel); + panel.add(uploadButton); + panel.add(clearButton); + + panel.add(dataViewScrollPane); + + uploadButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + byte[] eepromData = readDialogEEPROMBytes(null); + + // Write file data to EEPROM + if (eepromData != null) { + if (setEEPROMData(eepromData)) { + logger.info("Done! (" + eepromData.length + " bytes written to EEPROM)"); + } + + redrawDataView(dataViewArea); + } + } + }); + + clearButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + byte[] eepromData = new byte[EEPROM_SIZE]; + + if (setEEPROMData(eepromData)) { + logger.info("Done! (EEPROM reset to zero)"); + } + + redrawDataView(dataViewArea); + } + }); + + Observer observer; + this.addObserver(observer = new Observer() { + public void update(Observable obs, Object obj) { + long currentTime = mote.getSimulation().getSimulationTime(); + lastTimeLabel.setText("Last change at time: " + currentTime); + lastReadLabel.setText("Last change read bytes: " + getLastReadCount()); + lastWrittenLabel.setText("Last change wrote bytes: " + getLastWrittenCount()); + + redrawDataView(dataViewArea); + } + }); + + // Saving observer reference for releaseInterfaceVisualizer + panel.putClientProperty("intf_obs", observer); + + panel.setMinimumSize(new Dimension(140, 60)); + panel.setPreferredSize(new Dimension(140, 60)); + + dataViewArea.setLineWrap(false); + dataViewArea.setEditable(false); + dataViewArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + dataViewScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + dataViewScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + redrawDataView(dataViewArea); + + return panel; + } + + public void releaseInterfaceVisualizer(JPanel panel) { + Observer observer = (Observer) panel.getClientProperty("intf_obs"); + if (observer == null) { + logger.fatal("Error when releasing panel, observer is null"); + return; + } + + this.deleteObserver(observer); + } + + public Collection getConfigXML() { + Vector config = new Vector(); + Element element; + + // Infinite boolean + element = new Element("eeprom"); + element.setText(DatatypeConverter.printBase64Binary(getEEPROMData())); + config.add(element); + + return config; + } + + public void setConfigXML(Collection configXML, boolean visAvailable) { + for (Element element : configXML) { + if (element.getName().equals("eeprom")) { + setEEPROMData(DatatypeConverter.parseBase64Binary(element.getText())); + } + } + } + + /** + * Opens a file dialog and returns the contents of the selected file or null if dialog aborted. + * + * @param parent Dialog parent, may be null + * @return Binary contents of user selected file + */ + public static byte[] readDialogEEPROMBytes(Component parent) { + // Choose file + File file = null; + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setCurrentDirectory(new java.io.File(".")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setDialogTitle("Select binary data"); + + if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + file = fileChooser.getSelectedFile(); + } else { + return null; + } + + // Read file data + long fileSize = file.length(); + byte[] fileData = new byte[(int) fileSize]; + + FileInputStream fileIn; + DataInputStream dataIn; + int offset = 0; + int numRead = 0; + try { + fileIn = new FileInputStream(file); + dataIn = new DataInputStream(fileIn); + while (offset < fileData.length + && (numRead = dataIn.read(fileData, offset, fileData.length - offset)) >= 0) { + offset += numRead; + } + + dataIn.close(); + fileIn.close(); + } catch (Exception ex) { + logger.debug("Exception ex: " + ex); + return null; + } + + return fileData; + } + +}