/* * (C) Copyright 2015 by fr3ts0n <erwin.scheuch-heilig@gmx.at> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package com.fr3ts0n.ecu; import com.fr3ts0n.ecu.prot.obd.Messages; import org.apache.log4j.Logger; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Vector; /** * Collection of all known data items: * <pre> * The data structure looks as follows: * Service -- PID -- EcuDataItem * | |- EcuDataItem * | |- ... * ... |- PID -- EcuDataItem * | |- ... * |- ... -- ... * Service ... * </pre> * * @author erwin */ public class EcuDataItems extends HashMap<Integer, HashMap<Integer, Vector<EcuDataItem>>> { /** SerialVerion UID */ private static final long serialVersionUID = 5525561909111851836L; /** CSV field positions */ enum FLD { SVC, PID, OFS, LEN, BIT_OFS, BIT_LEN, BIT_MASK, FORMULA, FORMAT, MIN, MAX, MNEMONIC, LABEL, DESCRIPTION, NUMBEROFFIELDS } // set of all conversions public static EcuConversions cnv; // the data logger static Logger log = Logger.getLogger("data.items"); /** * Create data items from default CSV pidResource files * (prot/obd/res/pids.csv, prot/obd/res/conversions.csv) */ public EcuDataItems() { this("prot/obd/res/pids.csv", "prot/obd/res/conversions.csv"); } /** * Create data items from CSV pidResource file * @param pidResource resource file for PIDs (csv) * @param conversionResource resource file for conversions (csv) */ public EcuDataItems(String pidResource, String conversionResource) { cnv = new EcuConversions(conversionResource); loadFromResource(pidResource); } /** * read data from resource file into data structure * * @param resource the resource file (csv) */ private void loadFromResource(String resource) { try { loadFromStream(getClass().getResource(resource).openStream()); } catch (IOException e) { e.printStackTrace(); } } /** * read data from input stream (csv) into data structure * * @param inStr the csv input stream */ public void loadFromStream(InputStream inStr) { BufferedReader rdr; String currLine; String[] params; Conversion[] currCnvSet; EcuDataItem newItm; int line = 0; try { rdr = new BufferedReader(new InputStreamReader(inStr)); // loop through all lines of the file ... while ((currLine = rdr.readLine()) != null) { // ignore first line if (++line == 1 || currLine.startsWith("#")) //$NON-NLS-1$ { continue; } // repalce all optional quotes from CSV code list currLine = currLine.replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$ // split CSV line into parameters params = currLine.split("\t"); //$NON-NLS-1$ currCnvSet = cnv.get(params[FLD.FORMULA.ordinal()]); if (currCnvSet == null) { log.warn("Conversion not found: " + params[FLD.FORMULA.ordinal()] + " " + currLine); //$NON-NLS-1$ //$NON-NLS-2$ } // try to use MIN/MAX values from CSV Float minVal = null; Float maxVal = null; try { minVal = Float.parseFloat(params[FLD.MIN.ordinal()]); } catch(NumberFormatException ex) { /* ignore */ } try { maxVal = Float.parseFloat(params[FLD.MAX.ordinal()]); } catch(NumberFormatException e) { /* ignore */ } String label = Messages.getString(params[FLD.MNEMONIC.ordinal()], params[FLD.LABEL.ordinal()]); // create linear conversion newItm = new EcuDataItem(Integer.decode(params[FLD.PID.ordinal()]).intValue(), Integer.parseInt(params[FLD.OFS.ordinal()]), Integer.parseInt(params[FLD.LEN.ordinal()]), Integer.parseInt(params[FLD.BIT_OFS.ordinal()]), Integer.parseInt(params[FLD.BIT_LEN.ordinal()]), Long.decode(params[FLD.BIT_MASK.ordinal()]).longValue(), currCnvSet, params[FLD.FORMAT.ordinal()], minVal, maxVal, label); // enter data item for all specified services String[] services = params[FLD.SVC.ordinal()].split(","); //$NON-NLS-1$ for (String service : services) { int svcId = Integer.decode(service); appendItemToService(svcId, newItm); } } rdr.close(); } catch (IOException e) { e.printStackTrace(); } } /** * get all data items for selected service and PID * * @param service service to search data items for * @param pid pid to search data items for * @return Vector to data items - or null if no data items exist */ public Vector<EcuDataItem> getPidDataItems(int service, int pid) { Vector<EcuDataItem> currVec = null; HashMap<Integer, Vector<EcuDataItem>> currSvc = get(service); if (currSvc != null) { currVec = currSvc.get(pid); } return (currVec); } /** * get all data items for selected service * * @param service service to search data items for * @return Vector to data items - or null if no data items exist */ public Vector<EcuDataItem> getSvcDataItems(int service) { Vector<EcuDataItem> result = new Vector<EcuDataItem>(); HashMap<Integer, Vector<EcuDataItem>> currSvc = get(service); if (currSvc != null) { for(Vector<EcuDataItem> currVec : currSvc.values()) { result.addAll(currVec); } } return (result); } /** * append new data item to specified service * @param service service to add item to * @param newItem EcuDataItem to be added */ public void appendItemToService(int service, EcuDataItem newItem) { // check if service existes already HashMap<Integer, Vector<EcuDataItem>> currSvc = get(service); // if not - create it if (currSvc == null) { currSvc = new HashMap<Integer, Vector<EcuDataItem>>(); log.trace("+SVC: " + service + " - " + currSvc); //$NON-NLS-1$ //$NON-NLS-2$ } // check if item list exists for current PID Vector<EcuDataItem> currVec = currSvc.get(newItem.pid); // if not -- create it if (currVec == null) { currVec = new Vector<EcuDataItem>(); log.trace("+PID: " + newItem.pid + " - " + currVec); //$NON-NLS-1$ //$NON-NLS-2$ } // enter data item into list of items / PID currVec.add(newItem); // and update list in into the pid map for corresponding service currSvc.put(newItem.pid, currVec); // update map of services put(service, currSvc); // debug message of new enty log.trace("+" + service + "/" + String.format("0x%02X",newItem.pid) + " - " + currVec); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } /** * Update all EcuDataItems with new data from buffer * * @param service service of current data * @param pid pid of current data * @param buffer data buffer to do conversions on */ public void updateDataItems(int service, int pid, char[] buffer) { EcuDataItem currItm; Vector<EcuDataItem> currItms = getPidDataItems(service, pid); for (EcuDataItem currItm1 : currItms) { currItm = currItm1; currItm.updatePvFomBuffer(buffer); } } }