SLSS CarNet (Update 2021)

Die letzten Tage habe ich meine HTML-basierte Software des „SLSS CarNet Command Centers“ (Hauptartikel: SLSS CarNet*) weiter um- und ausgebaut. Nach der Installation der Hardware im Sommer 2020 konnte ich auf meinen Testfahrten (welche coronabedingt leider nicht so häufig waren wie gehofft) einiges an Erfahrungen und Eindrücken sammeln. Die ersten Erkenntnisse daraus habe ich nun in Form von Updates und Verbesserungen in die Software einfließen lassen. Das es an der einen oder anderen Stelle Verbesserungspotential geben wird, war mir bereits bei der Entwicklung der Soft- und Hardware klar. Ich war ziemlich erstaunt, dass sowohl die GPS-Ortung inkl. Richtungs- und Geschwindigkeitserkennung, als auch andere Komponenten, welche ich vorher nicht während der Fahrt (also „in Bewegung“) testen konnte, auf Anhieb funktionierten. Somit beschränken sich die Verbesserungen auch eher auf Optimierungen in Sachen Performance, Usability und Reaktionsgeschwindigkeit.

 

Verbesserung der CAN-Bus Reaktionsgeschwindigkeit dank Python

Eine Sache, welche mich schon beim Erstellen der Software beschäftigte, war die Erkennung der über den CAN-Bus gesendeten Botschaften. Ursprünglich nutze ich hierfür ein PHP-Script, welches per „shell_exec()„-Befehl das Lesen der CAN-Daten übernahm. Das Problem hierbei war zum einen, dass dieses Script nicht in einer Endlosschleife laufen konnte, da die so gesammelten Daten nach Zeit-X gespeichert werden mussten und dafür der Lesevorgang beendet werden musste. Zum anderen lief dieses Script innerhalb des „Command Centers“ was dazu führt, dass CAN-Daten nur erfasst wurden, wenn dieses auf einem Gerät ausgeführt wurde. 

Um diese Probleme zu lösen, entschied ich mich dazu die Funktionalität des Lesens der CAN-Botschaften auf ein Python-Script umzubauen. Dies bietet den großen Vorteil, dass es während der kompletten Laufzeit per Endlosschleife die Daten auf dem CAN-Bus analysieren kann. Dazu startet das Script mit dem ersten Start der GUI und läuft ab diesem Zeitpunkt unabhängig von der Bedienoberfläche. Die gelesen Daten werden innerhalb des Dauerloops sofort analysiert und anschließend über eine XML-Datei der GUI zur Verfügung gestellt. Dies verbesserte das Ansprechverhalten der GUI hinsichtlich geänderter Werte enorm. Das zuvor benötigte Fehlerhandling für das Schreiben der XML-Datei (die Datei wurde teilweise leer gespeichert) konnte ich ebenfalls minimieren. Dieses kommt jetzt nur noch bei evtl. Systemabstürzen während der Schreibphase der XML-Datei zum Einsatz. 

Beim Verbessern der CAN Funktionalität kam unteranderem meine CAN-Bus Analyse Software „SLSS CANAnalyser“ (Hauptartikel: SLSS CANAnalyser*“) zum Einsatz, welche ich für die Simulation bereits im Van verbauter und somit am „Basteltisch“ nicht mehr angeschlossener CAN-Hardware nutzte. So konnte zum Beispiel über das simulierte versenden von Temperatur-Botschaften das Schreiben und Lesen deutlich verbessern.

  

Screenshot der Reaktion der im Browser geöffneten SLSS CarNet GUI auf die per SLSS CANAnalyser versendeten CAN-Botschaften

 

Da ich die CAN-Botschaften beim Entwickeln des Temperatur-Boards selbst definiert und vor allem dokumentiert habe, wusste ich natürlich welche CAN-ID und Byte-Daten ich via CAN Software und Bus-Modul auf den Bus senden muss, um eine entsprechende Reaktion zu erhalten. Damit war es kein größeres Problem das Python-Script auf die ordnungsgemäße Funktion hin zu überprüfen. Möchte ich dem Projekt nun in Zukunft weitere Empfangsfunktionen hinzufügen, so kann ich dies durch das Erweitern dieses Scriptes ebenfalls recht unkompliziert realisieren. Das Script in seiner aktuellen Version sieht wie folgt aus.

  

import subprocess, threading, os, shutil
from xml.dom import minidom


class MyClass(threading.Thread):
    def __init__(self):
        self.stdout = None
        self.stderr = None
        threading.Thread.__init__(self)

    def run(self):
        p = subprocess.Popen(['timeout' ,'0.5', 'candump', 'can0'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        self.stdout, self.stderr = p.communicate()

#variable for tempoffset over CAN 
tempOffset = 30;

#infinite while loop
while(True):

	#copy Messwerte.Backup.xml if originalfile is broken
	if(os.stat('/var/www/html/Messwerte.xml').st_size == 0):
		shutil.copyfile('/var/www/html/Messwerte.Backup.xml', '/var/www/html/Messwerte.xml')
		print('Messwerte kopiert')
	else:
		print(os.stat('/var/www/html/Messwerte.xml').st_size)
		print('Messwerte nicht kopiert')

	#copy Controls.Backup.xml if originalfile is broken
	if(os.stat('/var/www/html/Controls.xml').st_size == 0):
		shutil.copyfile('/var/www/html/Controls.Backup.xml', '/var/www/html/Controls.xml')
		print('Controls kopiert')
	else:
		print(os.stat('/var/www/html/Controls.xml').st_size)
		print('Controls nicht kopiert')

	#new thread for reading candump
	myclass = MyClass()
	myclass.start()
	myclass.join()
	
	#load xmlfile Messwerte.xml
	myMeasure = minidom.parse('/var/www/html/Messwerte.xml')
		
	#read specific xml-Tag tag by tagName
	#print(myMeasure.getElementsByTagName('TempBoard2_Sens1')[0].firstChild.data)
		
	#split Can Messages
	myCan = str(myclass.stdout).split('\\n')
	
	#loop over all CA Messages
	for CanMsg in range(0, len(myCan)):		
		#try to split can0 to get Message only
		canStr = myCan[CanMsg].split("can0")
		
		#only if can0 is in CanStr
		if len(canStr) > 1:			
			#cut CAN Identifier and convert to decimal number
			canID = str(int(canStr[1][2:5], 16))
			
			canData = canStr[1].split("]")
			canData = canData[1].split(" ")
			
			#check submitted Board-ID --> canData[2] --> minimum value of 1 after subtracting 127 (if Value < 1 then its no Board answer)
			boardID = int(canData[2], 16) - 127
			
			
			#do some action if boardID matches some Values
			#Action Identification over --> genutzte send-ID (vom Board aus gesehen) from 01_Funktionsübersicht.xlsx (Boardliste)
			
			#IO Boards (Compact + 3-fach)
			if canID == '140':
				#IO Board sends Status of Input-Pins if Requested via CanCom.php
				print("IO Board Answer!")
				
			
			##################		
			#  4 Temp-Board  #
			##################
			if canID == '141':	
						
				#check if values from tempsensor 1+2 or 3+4
				sensorSelect = int(canData[3], 16)
				
				#check if sensors 1 & 2 were submitted
				if sensorSelect == 1:				
					#check if decimal places == 2
					nk1 = int(canData[5], 16)
					if nk1 < 10:
						nk1_out = "0" + str(nk1)
					else:
						nk1_out = str(nk1)
					
					#build tempstring for sensor 1
					tempval_1 = str(int(canData[4], 16) - tempOffset) + "," + nk1_out
					
					
					#check if decimal places == 2
					nk2 = int(canData[7], 16)
					if nk2 < 10:
						nk2_out = "0" + str(nk2)
					else:
						nk2_out = str(nk2)
					
					#build tempstring for sensor 2
					tempval_2 = str(int(canData[6], 16) - tempOffset) + "," + nk2_out

					#modify xml elements for sensor 1 and 2
					myMeasure.getElementsByTagName("TempBoard" + str(boardID) + "_Sens1")[0].childNodes[0].nodeValue = tempval_1
					myMeasure.getElementsByTagName("TempBoard" + str(boardID) + "_Sens2")[0].childNodes[0].nodeValue = tempval_2
				
				#check if sensors 3 & 4 were submitted
				if sensorSelect == 2:				
					#check if decimal places == 2
					nk1 = int(canData[5], 16)
					if nk1 < 10:
						nk1_out = "0" + str(nk1)
					else:
						nk1_out = str(nk1)
					
					#build tempstring for sensor 1
					tempval_3 = str(int(canData[4], 16) - tempOffset) + "," + nk1_out
					
					
					#check if decimal places == 2
					nk2 = int(canData[7], 16)
					if nk2 < 10:
						nk2_out = "0" + str(nk2)
					else:
						nk2_out = str(nk2)
					
					#build tempstring for sensor 2
					tempval_4 = str(int(canData[6], 16) - tempOffset) + "," + nk2_out

					#modify xml elements for sensor 1 and 2
					myMeasure.getElementsByTagName("TempBoard" + str(boardID) + "_Sens3")[0].childNodes[0].nodeValue = tempval_3
					myMeasure.getElementsByTagName("TempBoard" + str(boardID) + "_Sens4")[0].childNodes[0].nodeValue = tempval_4
			
			
			##################
			#  Camera Board  #
			##################
			if canID == '137':
				#load xmlfile Controls.xml
				myControls = minidom.parse('/var/www/html/Controls.xml')
				
				#check CamStat (1 == power activated | 0 == power deactivated)
				camStat = int(canData[3], 16)
				
				#check if in reverse gear
				reverseStat = int(canData[4], 16)
				
				#check running time
				timeToEnd = int(canData[6], 16)
				
				
				#if board-power is activated
				if camStat == 1:
					#if cam isnt active yet and also not manually closed, than activate
					if myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_ScreenAktiv")[0].firstChild.data == "0" and 
					#ACHTUNG: ZEILENUMBRUCH NUR FÜR DARSTELLUNG AUF HOMEPAGE!
					myControls.getElementsByTagName("Board_CamCom" + str(boardID) + "_ManOff")[0].firstChild.data == "0":
						#set display to active
						myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_ScreenAktiv")[0].childNodes[0].nodeValue = "1"
					
					#set cam to active
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_CamAktiv")[0].childNodes[0].nodeValue = "1"
				
				
				#if board-power is deactivated
				if camStat == 0:
					
					#always reset ManOff because Cam is off
					myControls.getElementsByTagName("Board_CamCom" + str(boardID) + "_ManOff")[0].childNodes[0].nodeValue = "0"
					
					#actualize Controls xml file
					with open("/var/www/html/Controls.xml", "w") as fs:
						myControls.writexml(fs) 
						fs.close()

					#if cam is active yet and should be closed, than deactivate
					if myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_ScreenAktiv")[0].firstChild.data == "1" and 
					#ACHTUNG: ZEILENUMBRUCH NUR FÜR DARSTELLUNG AUF HOMEPAGE!
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_CamAktiv")[0].firstChild.data == "0":
						#set display to inactive
						myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_ScreenAktiv")[0].childNodes[0].nodeValue = "0"
													
					#set cam to inactive
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_CamAktiv")[0].childNodes[0].nodeValue = "0"
				
				
				#if in reverse gear
				if reverseStat == 1:
					#set reverse to active
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_RFL")[0].childNodes[0].nodeValue = "1"
				
				else:
					#set reverse to active
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_RFL")[0].childNodes[0].nodeValue = "0"
				
				
				if timeToEnd != 0:
					#actualize running time
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_RestNachlauf")[0].childNodes[0].nodeValue = str(timeToEnd)
				
				else:
					#set running time to 0
					myMeasure.getElementsByTagName("Board_CamCom" + str(boardID) + "_RestNachlauf")[0].childNodes[0].nodeValue = "0"
			
			##################
			#  ENDE BOARDS	 #
			##################			
			
			##################
			#  DEBUG-OUTPUT  #
			##################
			print("Empfangen: " + canStr[1])
			print("ID: " + canID)
			print("Board-ID: " + str(boardID))
	
	
	
	#actualize Messwerte xml file
	with open("/var/www/html/Messwerte.xml", "w") as fs:
		myMeasure.writexml(fs) 
		fs.close()

 

Umbau von Here Maps API auf Google Maps Api

Ein weiterer Schritt war der Umbau meiner Navigationsansicht von der Here Maps Javascript Api* auf die Google Maps Api*. Da ich beim Erstellen der Software anfangs überhaupt keine Erfahrungswerte hatte, ob meine DIY-Navigationsfunktion überhaupt jemals sinnvoll nutzbar sein würde, steckte ich erst einmal nicht allzu viel Arbeit in diese Funktion. Ich suchte dafür nach einer schnellen und kostenlosen Möglichkeit meinen aktuellen Standort auf einer Karte anzeigen zu lassen und beschloss eine evtl. Navigationsfunktion wenn überhaupt erst nachträglich umzusetzen.

Da, wie bereits geschrieben, das Aktualisieren der Position über das verbaute GPS-Modul super funktionierte, entschloss ich mich dazu diese Funktion jetzt mit auf die to-do-Liste zu setzen. Nun hätte ich dies natürlich ebenfalls über die Here Maps Javascript Api* realisieren können, doch gefällt mir die Anzeige der bekannten Google-Maps irgendwie besser. Das brachte mich dazu mich in die  Google Maps Api* soweit einzuarbeiten, dass ich die vorhandene Anzeige der Here Maps durch die Karten von Google ersetzen und den Funktionsumfang wie gewünscht erweitern kann. 

Nach dem Erstellen eines API-Keys in der Google Developer Console* dauerte es eine Weile sich in der API-Doku und der Funktionsweise der einzelnen Komponenten zurechtzufinden. Hat man jedoch einmal den Dreh heraus und verstanden wie die API arbeitet und  man zusätzliche Daten, wie zum Beispiel Verkehrsdaten, Straßensperrungen und  Streckeninformationen anzeigen kann, dann macht es echt Spaß sich sein eigenes „Navigationssystem“ zu entwickeln.

 

Screenshot der „Navigationsfunktion“ über die Google Maps API

 

Warum „Navigationssystem“ mit Anführungszeichen

Leider stellt die Google Maps Api keine Point-to-Point Navigation bereit, so wie man es von einem Navigationssystem eigentlich gewohnt ist. So gibt es weder die Möglichkeit die aktuelle Position auf der Route zu setzen, noch die nächste Abbiegung abfangen zu können.

In meiner Umsetzung nutze ich für die aktuelle Position einen „Marker“, dessen Position automatisch durch die empfangenen GPS-Koordinaten aktualisiert wird. Zur Routenauswahl nutze ich die Google Directions Api*, welche nach dem Berechnen der Route als Overlay über die Karte der Maps API gelegt werden kann. Die Bedienung erfolgt über Bedienknöpfe, welche ich als eigenständige „divs“ auf der Karte platziert habe und bei Bedarf einblende oder aktualisiere. Für die Steuerung der Funktionen habe ich Java-Script Funktionen angelegt, da ich auf diese aus allen Bereichen der GUI zugreifen kann. 

Somit ist diese Umsetzung also kein klassisches Navigationssystem, sondern eher ein Routenvorschlag mit aktueller Positionsangabe. Für eine Aktualisierung der Strecke, wenn man zum Beispiel einmal die Route verlassen haben sollte, kann man einfach einen Button betätigen, womit eine Neuberechnung der Route ab der aktuellen Position durchgeführt wird. Mit diesen Einschränkungen kann ich denke ich ganz gut leben und falls Google den Funktionsumfang um die Point-to-Point Navigation erweitern sollte, steht einer Nachrüstung ja nichts im Wege. Der Vollständigkeit halber hier noch der aktuelle Code meines „Navigationssystems“.

Hinweis: Meinen API-Key habe ich entfernt und durch den Platzhalter „[API_KEY HIER]“ ersetzt!

 

<!DOCTYPE html>
<html>
  <head>
    <title>SLSS Navigation Map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style>
      /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
      #map {
        height: 100%;
      }
      /* Optional: Makes the sample page fill the window. */
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
	  .myButton {
	    /* Set CSS for my navigation buttons */
		background: none padding-box rgb(255, 255, 255);
		display: table-cell; 
		border-style: solid;
		border-width: 2px;
		border-color: #ffffff;
		border-radius: 3px;
		box-shadow: 0px 2px 6px rgba(0,0,0,.3);
		cursor: pointer;
		margin-top: 8px;
		margin-bottom: 22px;
	  }
	  .myText {
		text-align: center;
		vertical-align: middle; 
		color: rgb(86, 86, 86); 
		font-family: Roboto, Arial, sans-serif; 
		font-size: 18px;
		line-height: 38px;
		border-bottom-right-radius: 2px; 
		border-top-right-radius: 2px; 
	  } 
    </style>
  </head>
  <body>
	
	<!-- Map Layer -->
    <div style="position: absolute; width: 888px; height: 625px" id="map"></div>
	
	<!--Panel für Route -->
	<div id="right-panel" style="position: absolute; width: 301px; height: 405px; left: 550px; top:80px; background-color: #F5F5F5; overflow: scroll; display: none; opacity: 0.85;";></div>
	
	<!-- Button zoom + -->
	<div style="position: absolute; top: 382px; left:10px; width: 35px; height: 35px; display:block;" id="zoomPlus" class="myButton" onclick="doZoom(true);">
	<div id="zoomTextPlus" class="myText">+</div></div>
	
	<!-- Button zoom - -->
	<div style="position: absolute; top: 435px; left:10px; width: 35px; height: 35px; display:block;" id="zoomMinus" class="myButton" onclick="doZoom(false);">
	<div id="zoomTextMinus" class="myText">-</div></div>
	
	<!-- Button Routenoptionen -->
	<div style="position: absolute; top: 2px; left:215px; width: 172px; height: 35px; display: block;" id="myButton0" class="myButton" onclick="showControls();">
	<div id="myText0" class="myText">Routenoptionen</div></div>
	
	<!-- Button Navigation starten -->	
	<div style="position: absolute; top: 382px; left:10px; width: 188px; height: 35px; display: none; background: none padding-box rgb(0, 230, 0); border-color: rgb(0, 220, 0);; box-shadow: 0px 2px 6px rgba(0,210,0,.3);" id="myButton1" class="myButton" onclick="startNav();">
	<div id="myText1" class="myText">Zielführung starten</div></div>
	
	<!-- Button Navigation starten -->
	<div style="position: absolute; top: 435px; left:10px; width: 188px; height: 35px; display: none; background: none padding-box rgb(255, 0, 0); border-color: rgb(245, 0, 0);; box-shadow: 0px 2px 6px rgba(235,0,0,.3);" id="myButton2" class="myButton" onclick="abortNav();">
	<div id="myText2" class="myText">Zielführung abbrechen</div></div>
	
	<!-- Button hide / show Trackpoints  -->
	<div style="position: absolute; top: 2px; left:607px; width: 88px; height: 35px; display: none;" id="myButton3" class="myButton" onclick="showHideListView();">
	<div id="myText3" class="myText">Liste aus</div></div>
	
	<!-- Button aktuelle Position tracken  -->
	<div style="position: absolute; top: 2px; left:403px; width: 188px; height: 35px; display:block;" id="trackPos" class="myButton" onclick="freeMovement('button');">
	<div id="trackPosText" class="myText">Position fest</div></div>

<script>	
	//Variable declaration
	var latitude =  48.371835;
	var longitude = 11.519391599999999;
	var map, marker, myLatlng, directionsService, directionsRenderer, trafficLayer, savedDestination;
	var loaded = false;	
	var isNavigated = false;
	var freeToMove = false;
	var trafficSetted = true;
	var mapZoom = 18;

	//avoid highlighting
	function disableselect(e)
	{
		return false;
	}
	function reEnable()
	{
		return true;
	}
	
	document.onselectstart=new Function ("return false");
	if (window.sidebar)
	{
		document.onmousedown=disableselect;
		document.onclick=reEnable;
	}	

	//initialize map for navigation
	function initMap() 
	{
		map = new google.maps.Map(document.getElementById('map'), 
		{
		  center: {lat: latitude, lng: longitude},
		  zoom: mapZoom,
		  zoomControl: false,
		  fullscreenControl: false,
		  streetViewControl: false
		});		
		
		//set Marker of Homeposition
		myLatlng = new google.maps.LatLng(latitude, longitude);
		marker = new google.maps.Marker({ position: myLatlng, title:"StartMarker"});
		trafficLayer = new google.maps.TrafficLayer();
		
		// To add the marker to the map, call setMap();
		map.setCenter(marker.position);
		marker.setMap(map);	
		trafficLayer.setMap(map);	
		
		//set loaded variable to true
		loaded = true;
		freeMovement('false');
		
		/*
		//ONLY FOR DIRECTLY TESTING MY NAVIGATION-FUNCTION
		const locationButton = document.createElement("button");
		locationButton.textContent = "starte Navigationstest";
		locationButton.classList.add("custom-map-control-button");
		map.controls[google.maps.ControlPosition.TOP_CENTER].push(locationButton);
		locationButton.addEventListener("click", () => {calcRoute();});
		*/			
	}
	
	//show map after initialication
	function showMap(myLatitude, myLongitude)
	{ 
	   //check if map is loaded
	   if(loaded == true)
	   {	   
			//remove the old marker
			marker.setMap(null);
			
			//set latitude, longitude and markerposition for map
			myLatlng = new google.maps.LatLng(myLatitude, myLongitude);
			marker = new google.maps.Marker({ position: myLatlng, title:"Current GPS"});
			
			// To add the marker to the map, call setMap();
			marker.setMap(map);
			
			//check if map is free to move or centered over actual position
			if(freeToMove == false)
				map.setCenter(marker.position);
				
			//override values of latitude and longitude
			latitude = myLatitude;
			longitude = myLongitude;
	   }
	}

	//button for opening navigation menu
	function showControls() 
	{
		parent.iframeDel('HtmlMainMenu');
		parent.jsLink('NaviDestination.htm','HtmlMainMenu');
		parent.jsShow('ldheLayer_MainMenu',1);
		return false;
	}

	//Route calculation after entering destination
	function showHideListView() 
	{
		//only if navigation is setted
		if(isNavigated == true)
		{
			//check if right-panel is shown 
			if(document.getElementById('right-panel').style.display == 'block')
			{
				//hide panel with routeoptions
				document.getElementById('right-panel').style.display= 'none';
				document.getElementById('myText3').innerHTML = 'Liste an';				
			}
			else
			{
				//hide panel with routeoptions
				document.getElementById('right-panel').style.display= 'block';
				document.getElementById('myText3').innerHTML = 'Liste aus';	
			}	
		}
		else
		{
			//hide panel with routeoptions
			document.getElementById('right-panel').style.display= 'none';
			document.getElementById('myButton3').style.display= 'none';	
		}
	}

	//Route calculation after entering destination
	function calcRoute(destination, redirect = false) 
	{
		//try to abort old navigations
		if(isNavigated == true)
			abortNav();		
		
		//set loaded to false to avoid positioning while navi decision
		loaded = false;
		
		//init google directions Services (for routing)
		directionsService = new google.maps.DirectionsService();
		directionsRenderer = new google.maps.DirectionsRenderer();
		
		//set direction to google map
		directionsRenderer.setMap(map);
		directionsRenderer.setPanel(document.getElementById("right-panel"));
		
		//start and endpoint
		var start = myLatlng;
		var end = destination;
		var request = 
		{
			origin: start,
			destination: end,
			travelMode: 'DRIVING'
		};
		
		//if route request is OK show route
		directionsService.route(request, function(result, status) 
		{
			if (status == 'OK') 
			{
				directionsRenderer.setDirections(result);
				
				//add route to file with last destinations when not redirected
				if(redirect == false)
					parent.jsLink('NaviSetLastDestination.php?destination=' + destination,'Linkframe');
				
				//set destination to savedDestination for reset of startpoint
				savedDestination = destination;				
			}	
		});
		
		//show buttons for navigation start or navigation abort
		document.getElementById('myButton1').style.display= 'block';
		document.getElementById('myButton2').style.display= 'block';
		
		//show panel with routeoptions and set isNavigated to true for showing panel with stops
		document.getElementById('right-panel').style.display= 'block';
		isNavigated = true;
				
	}

	//start navigation 
	function startNav()
	{
		//hide buttons for navigation start or navigation abort
		document.getElementById('myButton1').style.display= 'none';
		document.getElementById('myButton2').style.display= 'none';
		
		//set text for buttn to hide listview and then show button
		document.getElementById('myText3').innerHTML = 'Liste aus';
		document.getElementById('myButton3').style.display= 'block';		
		
		//set loaded to true that map positioning starts to actual location
		loaded = true;
		
		//reset free movement
		freeMovement('false');
		
		//set zoom of map
		map.setZoom(mapZoom);
	}

	//abort navigation
	function abortNav()
	{
		//hide buttons for navigation start or navigation abort
		document.getElementById('myButton1').style.display= 'none';
		document.getElementById('myButton2').style.display= 'none';
		document.getElementById('myButton3').style.display= 'none';	
		
		//set loaded to true that map positioning starts to actual location
		loaded = true;
		
		//unload the direction route
		directionsRenderer.setMap(null);
		directionsRenderer.setPanel(null);
				
		//show panel with routeoptions and set isNavigated to true for showing panel with stops
		document.getElementById('right-panel').style.display= 'none';
		isNavigated = false;
		
		//reset free movement
		freeMovement('false');
		
		//set zoom of map
		map.setZoom(mapZoom);
	}
	
	//zoom map in and out
	function doZoom(inout)
	{
		//check to witch direction should be zoomed
		if(inout == true)
		{
			mapZoom += 1;			
		}
		else
		{
			mapZoom -= 1;
		}
		
		//set new zoom factor
		map.setZoom(mapZoom);
	}
	
	//function to enable / disable free map movement 
	function freeMovement(statustext)
	{
		if(document.getElementById('trackPosText').innerHTML == 'Position fest' && statustext == 'button')
		{
			document.getElementById('trackPosText').innerHTML = 'Position frei';
			freeToMove = true;
		}
		else
		{
			document.getElementById('trackPosText').innerHTML = 'Position fest';
			freeToMove = false;
		}
		
		if(statustext == 'false')
		{
			document.getElementById('trackPosText').innerHTML = 'Position fest';
			freeToMove = false;
		}
	}
	
	//reset Startpoint with actual position
	function reloadStartPoint()
	{
		//start route calculation with saved Destination
		if(savedDestination != null)
		{
			//calc route with redirect to true and start navigation immediately
			calcRoute(savedDestination, true);
			
			//set new zoom factor
			map.setZoom(mapZoom);
			
			//start navigation immediately
			startNav();
		}
	}

	//reset Startpoint with actual position
	function showHideTraffic()
	{
		//start route calculation with saved Destination
		if (trafficSetted == true)
		{
			trafficLayer.setMap(null);
			trafficSetted = false;
		}
		else
		{
			trafficLayer.setMap(map);
			trafficSetted = true;
		}
	}	

</script>

<!-- API KEY WURDE IN FOLGENDER ZEILE ENTFERNT -->
<script src="https://maps.googleapis.com/maps/api/js?key=[API_KEY HIER]&callback=initMap"async defer></script>
</body>
</html>

 

Da ich mit dem hier gezeigte Code noch nicht zu 100% fertig bin, habe ich mich noch nicht um eine durchgängig hübsche Formatierung gekümmert und das eine oder andere Kommentar fehlt vielleicht auch noch. Das Hin- und Herspringen zwischen deutsch- und englischsprachigen Kommentaren versuche ich mir auch gerade abzugewöhnen 🙂 .  Sollte es Fragen zum Code geben, können diese jedenfalls gern in die Kommentare gepostet werden. 

 

Fazit

Ich denke ich habe mit den aktuellen Änderungen / Erweiterungen das Projekt ein ganzes Stück voran gebracht. Jetzt steht dem Einbau weiterer Hardware-Komponenten nichts mehr im Weg und ich denke bereits über eine kompakter und platzsparendere Version meiner Hauptplatine nach, da ich auch hier am Anfang lieber mehr Platz gelassen habe, um auf evtl. Änderungen noch reagieren zu können.

Das ganze Thema ist schon ein wenig „Nerdi“, was den Kreis zum Fachsimpeln von vorn herein schon mal einschränkt. Solltet also jemand etwas ähnliches vorhaben, oder schon mitten in solch einem Projekt stecken, so bin ich an einem Erfahrungsaustausch jederzeit interessiert. 

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.