package org.goko.core.gcode.rs274ngcv3.jogl;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.goko.core.common.exception.GkException;
import org.goko.core.common.exception.GkTechnicalException;
import org.goko.core.common.service.AbstractGokoService;
import org.goko.core.common.service.IGokoService;
import org.goko.core.common.utils.CacheByKey;
import org.goko.core.controller.ICoordinateSystemAdapter;
import org.goko.core.controller.IFourAxisControllerAdapter;
import org.goko.core.controller.IGCodeContextProvider;
import org.goko.core.gcode.element.IGCodeProvider;
import org.goko.core.gcode.execution.ExecutionQueue;
import org.goko.core.gcode.execution.ExecutionQueueType;
import org.goko.core.gcode.execution.ExecutionToken;
import org.goko.core.gcode.execution.ExecutionTokenState;
import org.goko.core.gcode.execution.IExecutionToken;
import org.goko.core.gcode.rs274ngcv3.IRS274NGCService;
import org.goko.core.gcode.rs274ngcv3.context.GCodeContext;
import org.goko.core.gcode.rs274ngcv3.element.InstructionProvider;
import org.goko.core.gcode.rs274ngcv3.jogl.internal.Activator;
import org.goko.core.gcode.rs274ngcv3.jogl.internal.BaseGCodeContextProvider;
import org.goko.core.gcode.rs274ngcv3.jogl.internal.GCodeContextProviderLinkedList;
import org.goko.core.gcode.rs274ngcv3.jogl.internal.LinkedGCodeContextProvider;
import org.goko.core.gcode.rs274ngcv3.jogl.renderer.RS274GCodeRenderer;
import org.goko.core.gcode.service.IExecutionQueueListener;
import org.goko.core.gcode.service.IExecutionService;
import org.goko.core.gcode.service.IGCodeProviderRepositoryListener;
import org.goko.core.log.GkLog;
import org.goko.core.math.BoundingTuple6b;
import org.goko.tools.viewer.jogl.utils.render.basic.BoundsRenderer;
import org.goko.tools.viewer.jogl.utils.render.coordinate.CoordinateSystemSetRenderer;
public class RS274NGCV3JoglService extends AbstractGokoService implements IGokoService, IGCodeProviderRepositoryListener, IExecutionQueueListener<ExecutionTokenState, ExecutionToken<ExecutionTokenState>>{
/** LOG */
private static final GkLog LOG = GkLog.getLogger(RS274NGCV3JoglService.class);
/** ID of the service */
private static final String SERVICE_ID = "org.goko.core.gcode.rs274ngcv3.jogl.RS274NGCV3JoglService";
/** The list of managed renderer */
private CacheByKey<IGCodeProvider, RS274GCodeRenderer> cacheRenderer;
/** The list of managed renderer */
private CacheByKey<IExecutionToken, RS274GCodeRenderer> cacheRendererByExecutionToken;
/** The RS274 GCode service */
private IRS274NGCService rs274Service;
/** The bounds of all the loaded gcode */
private BoundsRenderer contentBoundsRenderer;
/** ICoordinateSystemAdapter */
private ICoordinateSystemAdapter coordinateSystemAdapter;
/** ICoordinateSystemAdapter */
private IGCodeContextProvider<GCodeContext> gcodeContextProvider;
/** Execution service */
private IExecutionService<ExecutionTokenState, ExecutionToken<ExecutionTokenState>> executionService;
/** The IFourAxisControllerAdapter */
private IFourAxisControllerAdapter fourAxisControllerAdapter;
/** The linked list of Context provider */
private GCodeContextProviderLinkedList lstContextProvider;
/**
* Constructor
*/
public RS274NGCV3JoglService() {
this.lstContextProvider = new GCodeContextProviderLinkedList();
this.cacheRenderer = new CacheByKey<IGCodeProvider, RS274GCodeRenderer>();
this.cacheRendererByExecutionToken = new CacheByKey<IExecutionToken, RS274GCodeRenderer>();
}
/** (inheritDoc)
* @see org.goko.core.common.service.IGokoService#getServiceId()
*/
@Override
public String getServiceId() throws GkException {
return SERVICE_ID;
}
/** (inheritDoc)
* @see org.goko.core.common.service.IGokoService#start()
*/
@Override
public void startService() throws GkException {
CoordinateSystemSetRenderer csrenderer = new CoordinateSystemSetRenderer();
csrenderer.setAdapter(coordinateSystemAdapter);
Activator.getJoglViewerService().addRenderer(csrenderer);
}
/** (inheritDoc)
* @see org.goko.core.common.service.IGokoService#stop()
*/
@Override
public void stopService() throws GkException {
}
/**
* Update content bounds
* Synchronized keyword is required to avoid conflicting update
* @throws GkException GkException
*/
private synchronized void updateContentBounds() throws GkException {
List<RS274GCodeRenderer> lstRenderer = cacheRenderer.get();
lstRenderer.addAll(cacheRendererByExecutionToken.get());
if(contentBoundsRenderer != null){
contentBoundsRenderer.destroy();
LOG.info("Ivoking destroy on ["+contentBoundsRenderer.toString()+"]");
//Activator.getJoglViewerService().removeRenderer(contentBoundsRenderer);
}
if(CollectionUtils.isNotEmpty(lstRenderer)){
BoundingTuple6b result = null;
for (RS274GCodeRenderer renderer : lstRenderer) {
if(renderer.getBounds() == null){ // Only update if bound is null
IGCodeProvider provider = renderer.getGCodeProvider();
InstructionProvider instructionProvider = Activator.getRS274NGCService().getInstructions(new GCodeContext(), provider);
BoundingTuple6b bounds = Activator.getRS274NGCService().getBounds(new GCodeContext(), instructionProvider);
renderer.setBounds(bounds);
}
if(result == null){
result = new BoundingTuple6b(renderer.getBounds());
}else{
result.add(renderer.getBounds());
}
}
contentBoundsRenderer = new BoundsRenderer(result);
Activator.getJoglViewerService().addRenderer(contentBoundsRenderer);
}
}
/**
* Creates the renderer for the given GCodeProvider
* @param idGCodeProvider the id of the GCodeProvider
* @throws GkException GkException
*/
public void createRenderer(IGCodeProvider provider) throws GkException{
RS274GCodeRenderer renderer = new RS274GCodeRenderer(provider, gcodeContextProvider, fourAxisControllerAdapter);
executionService.addExecutionListener(ExecutionQueueType.DEFAULT, renderer);
this.cacheRenderer.add(provider, renderer);
Activator.getJoglViewerService().addRenderer(renderer);
}
/**
* Creates the renderer for the given GCodeProvider
* @param idGCodeProvider the id of the GCodeProvider
* @return the created RS274GCodeRenderer
* @throws GkException GkException
*/
public RS274GCodeRenderer createRenderer(IExecutionToken executionToken) throws GkException{
RS274GCodeRenderer renderer = new RS274GCodeRenderer(executionToken.getGCodeProvider(), gcodeContextProvider, fourAxisControllerAdapter);
Activator.getJoglViewerService().addRenderer(renderer);
return renderer;
}
public void updateRenderer(IGCodeProvider gcodeProvider) throws GkException{
RS274GCodeRenderer renderer = getRendererByGCodeProvider(gcodeProvider);
renderer.updateGeometry();
}
/**
* Removes the renderer for the given GCodeProvider
* @param gcodeProvider the gcode provider
* @throws GkException GkException
*/
public void removeRenderer(IGCodeProvider gcodeProvider) throws GkException{
RS274GCodeRenderer renderer = findRendererByGCodeProvider(gcodeProvider);
if(renderer != null){
cacheRenderer.remove(gcodeProvider);
renderer.destroy();
}
}
/**
* Returns the renderer for the given gcodeProvider
* @param gcodeProvider the gcode provider
* @return an RS274GCodeRenderer
* @throws GkException GkException
*/
public RS274GCodeRenderer getRendererByGCodeProvider(IGCodeProvider gcodeProvider) throws GkException{
RS274GCodeRenderer renderer = findRendererByGCodeProvider(gcodeProvider);
if(renderer == null){
throw new GkTechnicalException("Renderer for GCodeProvider with internal id ["+gcodeProvider.getId()+"] does not exist");
}
return renderer;
}
/**
* Returns the renderer for the given IExecutionToken
* @param gcodeProvider the gcode provider
* @return an RS274GCodeRenderer
* @throws GkException GkException
*/
public RS274GCodeRenderer getRendererByExecutionToken(IExecutionToken token) throws GkException{
RS274GCodeRenderer renderer = findRendererByExecutionToken(token);
if(renderer == null){
throw new GkTechnicalException("Renderer for IExecutionToken with internal id ["+token.getId()+"] does not exist");
}
return renderer;
}
/**
* Returns the renderer for the given gcodeProvider
* @param idGCodeProvider the id of the gcode provider
* @return an RS274GCodeRenderer or <code>null</code> if none found
* @throws GkException GkException
*/
public RS274GCodeRenderer findRendererByGCodeProvider(IGCodeProvider gcodeProvider) throws GkException{
return cacheRenderer.find(gcodeProvider);
}
/**
* Returns the renderer for the given execution token
* @param idGCodeProvider the id of the gcode provider
* @return an RS274GCodeRenderer or <code>null</code> if none found
* @throws GkException GkException
*/
public RS274GCodeRenderer findRendererByExecutionToken(IExecutionToken token) throws GkException{
return cacheRendererByExecutionToken.find(token);
}
/**
* @param service the IRS274NGCService to set
* @throws GkException GkException
*/
public void setRS274NGCService(IRS274NGCService service) throws GkException{
this.rs274Service = service;
}
/**
* Returns the current IRS274NGCService
* @return IRS274NGCService
*/
public IRS274NGCService getRS274NGCService(){
return this.rs274Service;
}
/**
* @return the executionService
*/
public IExecutionService<ExecutionTokenState, ExecutionToken<ExecutionTokenState>> getExecutionService() {
return executionService;
}
/**
* @param executionService the executionService to set
* @throws GkException GkException
*/
public void setExecutionService(IExecutionService<ExecutionTokenState, ExecutionToken<ExecutionTokenState>> executionService) throws GkException {
this.executionService = executionService;
executionService.addExecutionQueueListener(this);
}
/**
* @return the coordinateSystemAdapter
*/
protected ICoordinateSystemAdapter getCoordinateSystemAdapter() {
return coordinateSystemAdapter;
}
/**
* @param coordinateSystemAdapter the coordinateSystemAdapter to set
*/
protected void setCoordinateSystemAdapter(ICoordinateSystemAdapter coordinateSystemAdapter) {
this.coordinateSystemAdapter = coordinateSystemAdapter;
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IGCodeProviderRepositoryListener#onGCodeProviderCreate(org.goko.core.gcode.element.IGCodeProvider)
*/
@Override
public void onGCodeProviderCreate(IGCodeProvider provider) throws GkException {
createRenderer(provider);
updateContentBounds();
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IGCodeProviderRepositoryListener#onGCodeProviderUpdate(org.goko.core.gcode.element.IGCodeProvider)
*/
@Override
public void onGCodeProviderUpdate(IGCodeProvider provider) throws GkException {
RS274GCodeRenderer renderer = findRendererByGCodeProvider(provider);
if(renderer != null){
renderer.setBounds(null); // Force update by setting bounds to null
updateRenderer(provider);
updateContentBounds();
}
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IGCodeProviderRepositoryListener#afterGCodeProviderDelete(org.goko.core.gcode.element.IGCodeProvider)
*/
@Override
public void afterGCodeProviderDelete(IGCodeProvider provider) throws GkException {
// Nothing yet
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IGCodeProviderRepositoryListener#beforeGCodeProviderDelete(org.goko.core.gcode.element.IGCodeProvider)
*/
@Override
public void beforeGCodeProviderDelete(IGCodeProvider provider) throws GkException {
removeRenderer(provider);
updateContentBounds();
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IGCodeProviderRepositoryListener#onGCodeProviderLocked(org.goko.core.gcode.element.IGCodeProvider)
*/
@Override
public void onGCodeProviderLocked(IGCodeProvider provider) throws GkException { }
/** (inheritDoc)
* @see org.goko.core.gcode.service.IGCodeProviderRepositoryListener#onGCodeProviderUnlocked(org.goko.core.gcode.element.IGCodeProvider)
*/
@Override
public void onGCodeProviderUnlocked(IGCodeProvider provider) throws GkException { }
/** (inheritDoc)
* @see org.goko.core.gcode.service.IExecutionQueueListener#onTokenCreate(org.goko.core.gcode.execution.IExecutionToken)
*/
@Override
public void onTokenCreate(ExecutionToken<ExecutionTokenState> token) {
try{
RS274GCodeRenderer renderer = findRendererByGCodeProvider(token.getGCodeProvider());
if(renderer == null){
// Create a renderer from the execution token itself
renderer = createRenderer(token);
}
// Ad the renderer as listener for execution
executionService.addExecutionListener(ExecutionQueueType.DEFAULT, renderer);
executionService.addExecutionListener(ExecutionQueueType.SYSTEM, renderer); // FIXME : remove double listener addition
cacheRendererByExecutionToken.add(token, renderer);
// Make sure we get the updated context from latest executed token
if(lstContextProvider.isEmpty()){
lstContextProvider.add(new BaseGCodeContextProvider(gcodeContextProvider, token, rs274Service));
}
// We link the GCodeContext to the context right after the previous token execution
renderer.setGCodeContextProvider(lstContextProvider.getLast());
LinkedGCodeContextProvider contextProvider = new LinkedGCodeContextProvider(lstContextProvider.getLast(), token, rs274Service);
lstContextProvider.addLast(contextProvider);
renderer.updateGeometry();
updateContentBounds();
}catch(GkException e){
LOG.error(e);
}
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IExecutionQueueListener#onTokenDelete(org.goko.core.gcode.execution.IExecutionToken)
*/
@Override
public void onTokenDelete(ExecutionToken<ExecutionTokenState> token) {
try{
LinkedGCodeContextProvider contextProvider = lstContextProvider.findExecutionTokenByIdExecutionToken(token.getId());
LinkedGCodeContextProvider childContextProvider = lstContextProvider.findExecutionTokenAfter(token.getId());
// Search the matching context provider
if(contextProvider != null){
lstContextProvider.remove(contextProvider);
if(childContextProvider != null){
RS274GCodeRenderer childRenderer = getRendererByExecutionToken(childContextProvider.getToken());
childContextProvider.setPrevious(contextProvider.getPrevious());
// Remap the renderer of the child token to link on the new parent
childRenderer.setGCodeContextProvider(childContextProvider.getPrevious());
childContextProvider.update();
}
}
// Update the rendering of the GCodeProvider
RS274GCodeRenderer renderer = findRendererByExecutionToken(token);
if(renderer != null){
executionService.removeExecutionListener(renderer);
cacheRendererByExecutionToken.remove(token);
// Do we have a renderer for the provider itself ?
RS274GCodeRenderer providerRenderer = findRendererByGCodeProvider(token.getGCodeProvider());
if(providerRenderer != null){
// Set the deleted token's renderer provider back to default
renderer.setGCodeContextProvider(gcodeContextProvider);
renderer.updateGeometry();
}else{
renderer.destroy();
}
}
updateGeometryRendererInExecutionQueue();
updateContentBounds();
}catch(GkException e){
LOG.error(e);
}
}
/** (inheritDoc)
* @see org.goko.core.gcode.service.IExecutionQueueListener#onTokenUpdate(org.goko.core.gcode.execution.IExecutionToken)
*/
@Override
public void onTokenUpdate(ExecutionToken<ExecutionTokenState> token) {
try{
LinkedGCodeContextProvider contextProvider = lstContextProvider.findExecutionTokenByIdExecutionToken(token.getId());
// Search the matching context provider
// Iterator<LinkedGCodeContextProvider> iter = lstContextProvider.descendingIterator();
// while (iter.hasNext()) {
// LinkedGCodeContextProvider lclContextProvider = (LinkedGCodeContextProvider) iter.next();
// if(ObjectUtils.equals(lclContextProvider.getToken().getId(), token.getId())){
// contextProvider = lclContextProvider;
// break;
// }
// }
if(contextProvider != null){
contextProvider.update();
updateGeometryRendererInExecutionQueue();
updateContentBounds();
}
}catch(GkException e){
LOG.error(e);
}
}
/**
* Force a redraw of the renderer in the execution queue
* @throws GkException GkException
*/
public void updateGeometryRendererInExecutionQueue() throws GkException {
for(ExecutionQueue<ExecutionTokenState, ExecutionToken<ExecutionTokenState>> queue : executionService.getExecutionQueue()){
List<ExecutionToken<ExecutionTokenState>> lstTokens = queue.getExecutionToken();
if(CollectionUtils.isNotEmpty(lstTokens)){
for (ExecutionToken<ExecutionTokenState> token : lstTokens) {
RS274GCodeRenderer renderer = getRendererByExecutionToken(token);
renderer.updateGeometry();
}
}
}
}
/**
* @return the gcodeContextProvider
*/
public IGCodeContextProvider<GCodeContext> getGCodeContextProvider() {
return gcodeContextProvider;
}
/**
* @param gcodeContextProvider the gcodeContextProvider to set
*/
public void setGCodeContextProvider(IGCodeContextProvider<GCodeContext> gcodeContextProvider) {
this.gcodeContextProvider = gcodeContextProvider;
}
/**
* @return the fourAxisControllerAdapter
*/
public IFourAxisControllerAdapter getFourAxisControllerAdapter() {
return fourAxisControllerAdapter;
}
/**
* @param fourAxisControllerAdapter the fourAxisControllerAdapter to set
*/
public void setFourAxisControllerAdapter(IFourAxisControllerAdapter fourAxisControllerAdapter) {
this.fourAxisControllerAdapter = fourAxisControllerAdapter;
}
}