blob: 57c50803a5d10839d76e9811cb78621824eea298 [file] [log] [blame]
/*
*
* Copyright (c) 2011 - 2016 - Loetz GmbH & Co KG, 69115 Heidelberg, Germany
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Initial contribution:
* Loetz GmbH & Co. KG
*
*/
package org.eclipse.osbp.abstractstatemachine;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.channels.SelectableChannel;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.PreDestroy;
import javax.swing.Timer;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.osbp.dsl.common.datatypes.IDto;
import org.eclipse.osbp.preferences.ProductConfiguration;
import org.eclipse.osbp.runtime.common.filter.IQuery;
import org.eclipse.osbp.runtime.common.session.ISession;
import org.eclipse.osbp.ui.api.customfields.IBlobService;
import org.eclipse.osbp.ui.api.message.IMessageRequester;
import org.eclipse.osbp.ui.api.message.MessageEvent;
import org.eclipse.osbp.ui.api.message.MessageEvent.EventType;
import org.eclipse.osbp.ui.api.message.MessageRequesterEvent;
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService;
import org.eclipse.osbp.ui.api.report.IReportProvider;
import org.eclipse.osbp.ui.api.statemachine.IDataProvider;
import org.eclipse.osbp.ui.api.statemachine.IEventSource;
import org.eclipse.osbp.ui.api.statemachine.IPeripheral;
import org.eclipse.osbp.ui.api.statemachine.IStateMachine;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService;
import org.eclipse.osbp.ui.api.user.IUser;
import org.eclipse.osbp.utils.vaadin.MessageDialog;
import org.eclipse.osbp.utils.vaadin.YesNoDialog;
import org.eclipse.osbp.vaadin.addons.keyevents.KeyEventExtension;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.server.Page;
import com.vaadin.server.WebBrowser;
import com.vaadin.ui.Component;
import com.vaadin.ui.HasComponents;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
public abstract class AbstractStateMachine implements IStateMachine, IMessageRequester.ClickRecipient {
private static final String ENABLED = "Enabled";
private static Logger logger = org.slf4j.LoggerFactory.getLogger("statemachine");
protected Collection<IEventSource.Enabler> enablerList = new CopyOnWriteArraySet<>();
protected Collection<IDataProvider.Provider> providerList = new CopyOnWriteArraySet<>();
protected Collection<IPeripheral.Command> peripheralList = new CopyOnWriteArraySet<>();
protected Map<String, MessageDialog> messageDialogs = new HashMap<>();
protected Map<String, YesNoDialog> yesnoDialogs = new HashMap<>();
protected WebBrowser webBrowser = Page.getCurrent().getWebBrowser();
protected IUser user;
protected Locale locale;
protected IDSLMetadataService dslMetadataService;
protected IThemeResourceService themeResourceService;
protected IReportProvider reportProvider;
protected IBlobService blobService;
protected Map<String, Map<String, Object>> storage = new HashMap<>();
protected SelectableChannel channel;
protected String lastTrigger;
protected IEclipseContext eclipseContext;
protected LinkedBlockingQueue<MessageEvent> queue = new LinkedBlockingQueue<>();
protected boolean remotePortOverridden = false;
protected Map<Integer, String> keyMapper = new HashMap<Integer, String>() {
private static final long serialVersionUID = 8801421284339992078L;
{
put(KeyCode.TAB, "TAB");
put(KeyCode.ENTER, "ENTER");
put(KeyCode.PAGE_UP, "PGUP");
put(KeyCode.PAGE_DOWN, "PGDOWN");
put(KeyCode.BACKSPACE, "BACKSPACE");
put(KeyCode.INSERT, "INSERT");
put(KeyCode.DELETE, "DELETE");
put(KeyCode.HOME, "HOME");
put(KeyCode.END, "END");
put(KeyCode.ARROW_DOWN, "DOWNARROW");
put(KeyCode.ARROW_UP, "UPARROW");
put(KeyCode.ARROW_LEFT, "LEFTARROW");
put(KeyCode.ARROW_RIGHT, "RIGHTARROW");
put(KeyCode.ESCAPE, "ESC");
put(KeyCode.F1, "F1");
put(KeyCode.F2, "F2");
put(KeyCode.F3, "F3");
put(KeyCode.F4, "F4");
put(KeyCode.F5, "F5");
put(KeyCode.F6, "F6");
put(KeyCode.F7, "F7");
put(KeyCode.F8, "F8");
put(KeyCode.F9, "F9");
put(KeyCode.F10, "F10");
put(KeyCode.F11, "F11");
put(KeyCode.F12, "F12");
}
};
private KeyEventExtension extension = null;
private AtomicBoolean active = new AtomicBoolean(false);
private AtomicBoolean blocking = new AtomicBoolean(false);
private int remotePort = 9090;
@Override
public void installKeyEventConsumer(VerticalLayout layout) {
extension = KeyEventExtension.addTo(layout);
extension.setCallback(this::keyPressed);
}
@Override
public void uninstallKeyEventConsumer() {
if (extension != null) {
extension.remove();
extension = null;
}
}
protected Component findById(HasComponents root, String id) {
Iterator<Component> iterate = root.iterator();
while (iterate.hasNext()) {
Component c = iterate.next();
if (id.equals(c.getId())) {
return c;
}
if (c instanceof HasComponents) {
Component cc = findById((HasComponents) c, id);
if (cc != null)
return cc;
}
}
return null;
}
protected void keyPressed(org.eclipse.osbp.vaadin.addons.keyevents.client.Event e) {
if (keyMapper.containsKey(e.charCode)) {
logger.debug("keymapper code {}", keyMapper.get(e.charCode));
processEvent(this, new MessageEvent(EventType.FUNCTIONALKEYBOARD, keyMapper.get(e.charCode)));
} else {
if(e.charCode > 95 && e.charCode < 106 ) {
e.charCode -= 48;
} else if(!e.shiftKeyDown && e.charCode > 57 && e.charCode < 91) {
e.charCode += 32;
} else if(e.shiftKeyDown && e.charCode > 47 && e.charCode < 58) {
e.charCode -= 16;
} else if(e.charCode == 192) { // ö
if(e.shiftKeyDown) {
e.charCode = 0xD6;
} else {
e.charCode = 0xF6;
}
} else if(e.charCode == 222) { // ä
if(e.shiftKeyDown) {
e.charCode = 0xC4;
} else {
e.charCode = 0xE4;
}
} else if(e.charCode == 186) { // ü
if(e.shiftKeyDown) {
e.charCode = 0xDC;
} else {
e.charCode = 0xFC;
}
} else if(e.charCode == 219) { // ß
if(e.shiftKeyDown) {
e.charCode = 63;
} else {
e.charCode = 0xDF;
}
}
if (Character.isLetterOrDigit(e.charCode) || Character.isSpace((char)e.charCode)) {
String evtCode = Character.toString((char) e.charCode);
logger.debug("alpha or digit {}", evtCode);
processEvent(this, new MessageEvent(EventType.KEYBOARD, Character.toString((char) e.charCode)));
}
}
}
@Override
public void processEvent(final IStateMachine statemachine, final MessageEvent event) {
synchronized (queue) {
logger.debug("enqueue event {}", event);
queue.add(event);
}
}
protected abstract void doProcessEvent(final IStateMachine statemachine, final MessageEvent event);
private void addEventProvider(IEventSource.Enabler listener) {
enablerList.add(listener);
}
private void removeEventProvider(IEventSource.Enabler listener) {
enablerList.remove(listener);
}
@Override
public void registerEnabler(IEventSource.Enabler listener) {
addEventProvider(listener);
}
@Override
public void unregisterEnabler(IEventSource.Enabler listener) {
removeEventProvider(listener);
}
@Override
public void setUser(IUser user) {
this.user = user;
}
@Override
public IUser getUser() {
return user;
}
@Override
public String getUserName() {
return user.getUserName();
}
@Override
public String getUserPassword() {
return user.getExtraPassword();
}
@Override
public String getUserEmail() {
return user.getEmail();
}
@Override
public String getUserPosition() {
return user.getPosition();
}
@Override
public String getUserPrintService() {
return user.getPrintService();
}
@Override
public void setLocale(Locale locale) {
this.locale = locale;
}
@Override
public Locale getLocale() {
return locale;
}
@Override
public void setDslMetadataService(IDSLMetadataService dslMetadataService) {
this.dslMetadataService = dslMetadataService;
}
@Override
public IDSLMetadataService getDslMetadataService() {
return dslMetadataService;
}
@Override
public IThemeResourceService getThemeResourceService() {
return themeResourceService;
}
@Override
public void setThemeResourceService(IThemeResourceService themeResourceService) {
this.themeResourceService = themeResourceService;
}
@Override
public void setReportProvider(IReportProvider reportprovider) {
this.reportProvider = reportprovider;
}
@Override
public IReportProvider getReportProvider() {
return reportProvider;
}
@Override
public IBlobService getBlobService() {
return blobService;
}
@Override
public void setBlobService(IBlobService blobService) {
this.blobService = blobService;
}
@Override
public void enable(String id, Boolean enable) {
for (IEventSource.Enabler listener : enablerList) {
listener.enable(listener, id, enable);
}
}
@Override
public void toggle(String id) {
for (IEventSource.Enabler listener : enablerList) {
listener.toggle(listener, id + ENABLED);
}
for (IDataProvider.Provider listener : providerList) {
listener.toggle(listener, id + ENABLED);
}
for (IPeripheral.Command listener : peripheralList) {
listener.toggle(listener, id + ENABLED);
}
}
@Override
public Object get(String id) {
Object value;
for (IEventSource.Enabler listener : enablerList) {
value = listener.get(listener, id);
if (value != null) {
return value;
}
}
for (IDataProvider.Provider listener : providerList) {
value = listener.get(listener, id);
if (value != null) {
return value;
}
}
for (IPeripheral.Command listener : peripheralList) {
value = listener.get(listener, id);
if (value != null) {
return value;
}
}
return null;
}
@Override
public Object get(String id, String parameter) {
Object value;
for (IEventSource.Enabler listener : enablerList) {
value = listener.get(listener, id, parameter);
if (value != null) {
return value;
}
}
for (IDataProvider.Provider listener : providerList) {
value = listener.get(listener, id, parameter);
if (value != null) {
return value;
}
}
for (IPeripheral.Command listener : peripheralList) {
value = listener.get(listener, id, parameter);
if (value != null) {
return value;
}
}
return null;
}
@Override
public void set(String id, Object content) {
for (IEventSource.Enabler listener : enablerList) {
listener.set(listener, id, content);
}
for (IDataProvider.Provider listener : providerList) {
listener.set(listener, id, content);
}
for (IPeripheral.Command listener : peripheralList) {
listener.set(listener, id, content);
}
}
@Override
public void set(String id, String device, Object content) {
for (IPeripheral.Command listener : peripheralList) {
listener.set(listener, id, device, content);
}
}
@Override
public void clear(String id) {
for (IEventSource.Enabler listener : enablerList) {
listener.clear(listener, id);
}
for (IDataProvider.Provider listener : providerList) {
listener.clear(listener, id);
}
for (IPeripheral.Command listener : peripheralList) {
listener.clear(listener, id);
}
}
@Override
public void append(String id, String key) {
for (IEventSource.Enabler listener : enablerList) {
listener.append(listener, id, key);
}
}
@Override
public void remove(String id, int pos) {
for (IEventSource.Enabler listener : enablerList) {
listener.remove(listener, id, pos);
}
}
@Override
public void caption(String id, String value) {
for (IEventSource.Enabler listener : enablerList) {
listener.caption(listener, id, value);
}
}
@Override
public void image(String id, String imageName) {
for (IEventSource.Enabler listener : enablerList) {
listener.image(listener, id, imageName);
}
}
@Override
public void addDataProvider(IDataProvider.Provider listener) {
providerList.add(listener);
}
@Override
public void removeDataProvider(IDataProvider.Provider listener) {
providerList.remove(listener);
}
@Override
public void registerDataProvider(IDataProvider.Provider listener) {
addDataProvider(listener);
}
@Override
public void unregisterDataProvider(IDataProvider.Provider listener) {
removeDataProvider(listener);
}
public void addPeripheral(IPeripheral.Command listener) {
peripheralList.add(listener);
}
public void removePeripheral(IPeripheral.Command listener) {
peripheralList.remove(listener);
}
@Override
public void registerPeripheral(IPeripheral.Command listener) {
addPeripheral(listener);
}
@Override
public void unregisterPeripheral(IPeripheral.Command listener) {
removePeripheral(listener);
}
@Override
public Boolean find(String dtoName, IQuery query) {
Boolean retVal;
for (IDataProvider.Provider listener : providerList) {
retVal = listener.find(listener, dtoName, query);
if (retVal != null) {
return retVal;
}
}
return false;
}
@Override
public Boolean find(String dtoName, String fieldName, Object search) {
Boolean retVal;
for (IDataProvider.Provider listener : providerList) {
retVal = listener.find(listener, dtoName, fieldName, search);
if (retVal != null) {
return retVal;
}
}
return false;
}
@Override
public Collection<? extends IDto> findAll(String dtoName, IQuery query) {
Collection<? extends IDto> retVal;
for (IDataProvider.Provider listener : providerList) {
retVal = listener.findAll(dtoName, query);
if (retVal != null) {
return retVal;
}
}
return Collections.emptyList();
}
@Override
public Collection<? extends IDto> findAll(String dtoName, String fieldName, Object search) {
Collection<? extends IDto> retVal;
for (IDataProvider.Provider listener : providerList) {
retVal = listener.findAll(dtoName, fieldName, search);
if (retVal != null) {
return retVal;
}
}
return Collections.emptyList();
}
@Override
public void addTo(String dtoName, String listName, IDto dto) {
for (IDataProvider.Provider listener : providerList) {
listener.addTo(listener, dtoName, listName, dto);
}
}
@Override
public void removeFrom(String dtoName, String listName, IDto dto) {
for (IDataProvider.Provider listener : providerList) {
listener.removeFrom(listener, dtoName, listName, dto);
}
}
@Override
public void update(String dtoName) {
for (IDataProvider.Provider listener : providerList) {
listener.update(listener, dtoName, (IDto) listener.get(listener, dtoName));
}
}
@Override
public void reload(String dtoName) {
for (IDataProvider.Provider listener : providerList) {
listener.reload(listener, dtoName, (IDto) listener.get(listener, dtoName));
}
}
@Override
public void delete(String dtoName) {
for (IDataProvider.Provider listener : providerList) {
listener.delete(listener, dtoName, (IDto) listener.get(listener, dtoName));
}
}
@Override
public String getIPAddress() {
// must define -Djava.net.preferIPv4Stack=true to always get IPv4 in
// dual stack environments
return webBrowser.getAddress(); // May be null, especially if running in
// a Portlet.
}
@Override
public String getHostName() {
String hostName = null;
try {
InetAddress netAddress = InetAddress.getByName(webBrowser.getAddress());
hostName = netAddress.getHostName().toLowerCase();
logger.error("**** browser returned this host-name: {} ****", hostName);
} catch (UnknownHostException e) {
logger.error("{}", e);
}
return hostName;
}
@Override
public int getRemotePort() {
if(!remotePortOverridden) {
if(ProductConfiguration.getJavaPosPortConfiguration() != null && !ProductConfiguration.getJavaPosPortConfiguration().isEmpty()) {
return Integer.parseInt(ProductConfiguration.getJavaPosPortConfiguration());
}
}
return remotePort;
}
@Override
public void setRemotePort(int port) {
remotePortOverridden = true;
this.remotePort = remotePort;
}
@Override
public boolean isItMe(String hostName) {
String hostAddress;
try {
InetAddress netAddress = InetAddress.getByName(hostName);
hostAddress = netAddress.getHostAddress();
logger.error("**** browser returned this host-address: {} ****", hostAddress);
} catch (UnknownHostException e) {
logger.error("{}", e);
return false;
}
boolean ret = getIPAddress().equals(hostAddress);
if(!ret) {
logger.error("**** browser host-address and requested host are not equal! Requested: {} ****", hostName);
}
return ret;
}
@Override
public DateTime getNow() {
return DateTime.now(DateTimeZone.UTC);
}
@Override
public String getUserAgentInfo() {
return webBrowser.getBrowserApplication();
}
@Override
public Boolean isTouchDevice() {
return webBrowser.isTouchDevice();
}
@Override
public int getSceenWidth() {
return webBrowser.getScreenWidth();
}
@Override
public int getScreenHeight() {
return webBrowser.getScreenHeight();
}
@PreDestroy
void destroy() {
}
@Override
public Locale getBrowserLocale() {
return webBrowser.getLocale();
}
@Override
public Boolean isHttps() {
return webBrowser.isSecureConnection();
}
@Override
public void start() {
active.set(true);
final UI ui = getEclipseContext().get(UI.class);
Runnable runnable = () -> {
while (active.get()) {
if(!blocking.get()) {
MessageEvent event;
try {
event = queue.take();
ui.access(() -> {
logger.debug("doProcess event {}", event);
doProcessEvent(AbstractStateMachine.this, event);
});
} catch (InterruptedException e) {
logger.debug("dequeuing was interrupted {}", e);
Thread.currentThread().interrupt();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO sleep interrupted
}
}
};
Thread thread = new Thread(runnable, "Statemachine processor " + getClass().getName());
thread.setDaemon(true);
thread.start();
}
@Override
public void schedule(IStateMachine statemachine, int milliseconds, MessageEvent event) {
Timer timer = new Timer(milliseconds, action -> statemachine.processEvent(statemachine, event));
timer.setRepeats(false);
timer.start();
}
/**
* @param doubleStr
* @return Double independent of locale
*/
@Override
public Double parseDouble(String doubleStr) {
String doubleStrIn = doubleStr.replaceAll("[^\\d,\\.]++", "");
if (doubleStrIn.length() == 0) {
return 0.0;
}
if (doubleStrIn.matches(".+\\.\\d+,\\d+$"))
return Double.parseDouble(doubleStrIn.replaceAll("\\.", "").replaceAll(",", "."));
if (doubleStrIn.matches(".+,\\d+\\.\\d+$"))
return Double.parseDouble(doubleStrIn.replaceAll(",", ""));
return Double.parseDouble(doubleStrIn.replaceAll(",", "."));
}
/**
* Format a double.
*
* @param value
* the value
* @param length
* the total length of the resulting string
* @param precision
* the precision after decimal separator
* @return the string
*/
@Override
public String formatDouble(Double value, int length, int precision) {
String format = String.format("%% %d.%df", length, precision);
return String.format(locale, format, value);
}
@Override
public String getTranslation(String token) {
if (dslMetadataService != null && locale != null) {
return dslMetadataService.translate(locale.toLanguageTag(), token);
}
return token;
}
@Override
public void closeMessage(String messageId) {
if (messageDialogs.containsKey(messageId)) {
MessageDialog messageDialog = messageDialogs.get(messageId);
messageDialog.close();
UI.getCurrent().removeWindow(messageDialog);
messageDialogs.remove(messageId);
}
}
@Override
public void openMessage(String messageId) {
openMessage(messageId, false);
}
@Override
public void openMessage(String messageId, boolean okButton) {
MessageDialog messageDialog = new MessageDialog(messageId, getTranslation(messageId));
if (okButton) {
messageDialog.addButton(getTranslation("ok"), new MessageEvent(EventType.MESSAGECLOSE, messageId));
messageDialog.addButtonListener(this);
}
messageDialogs.put(messageId, messageDialog);
UI.getCurrent().addWindow(messageDialog);
}
@Override
public void openQuestion(String messageId, EventType type, String yesEvent, String noEvent, String cancelEvent) {
MessageDialog messageDialog = new MessageDialog(messageId, getTranslation(messageId));
messageDialog.addButton(getTranslation("yes"), new MessageEvent(type, yesEvent));
messageDialog.addButton(getTranslation("no"), new MessageEvent(type, noEvent));
messageDialog.addButton(getTranslation("cancel"), new MessageEvent(type, cancelEvent));
messageDialog.addButtonListener(this);
messageDialogs.put(messageId, messageDialog);
UI.getCurrent().addWindow(messageDialog);
}
@Override
public void messageButtonPressed(MessageRequesterEvent e) {
MessageDialog messageDialog = messageDialogs.get(e.getId());
messageDialog.close();
UI.getCurrent().removeWindow(messageDialog);
messageDialogs.remove(e.getId());
this.processEvent(this, e.getEventData());
}
@Override
public void dispatchMessages(MessageEvent event) {
if (event.getType() == MessageEvent.EventType.TRIGGER) {
lastTrigger = event.getId();
}
switch (event.getType()) {
case ERROR:
openMessage(event.getId(), true);
break;
case MESSAGEOPEN:
openMessage(event.getId());
break;
case MESSAGECLOSE:
closeMessage(event.getId());
break;
case STATUSOPEN:
case STATUSCLOSE:
default:
break;
}
}
@Override
public Object getStorage(String key, String attribute) {
if (storage.containsKey(key) && storage.get(key).containsKey(attribute)) {
return storage.get(key).get(attribute);
}
return null;
}
@Override
public Set<String> getStorageAttributes(String key) {
if (storage.containsKey(key)) {
return storage.get(key).keySet();
}
return Collections.emptySet();
}
@Override
public void putStorage(String key, String attribute, Object content) {
if (!storage.containsKey(key)) {
storage.put(key, new HashMap<String, Object>());
}
storage.get(key).put(attribute, content);
}
@Override
public void openChannel(SelectableChannel channel) {
this.channel = channel;
}
@Override
public SelectableChannel getChannel() {
return channel;
}
@Override
public void closeChannel() {
if (channel != null) {
try {
channel.close();
} catch (IOException e) { // NOSONAR
// do nothing
}
}
}
@Override
public String getLastTrigger() {
return lastTrigger;
}
@Override
public void setEclipseContext(IEclipseContext eclipseContext) {
this.eclipseContext = eclipseContext;
this.dslMetadataService = eclipseContext.get(IDSLMetadataService.class);
this.themeResourceService = eclipseContext.get(IThemeResourceService.class);
this.reportProvider = eclipseContext.get(IReportProvider.class);
this.blobService = eclipseContext.get(IBlobService.class);
}
@Override
public IEclipseContext getEclipseContext() {
return eclipseContext;
}
@Override
public void sendSlaveData(Map<String, Object> data) {
String currentInstanceId = (String) getEclipseContext().get("e4ApplicationInstanceId");
List<ISession> sessions = POSServiceBinder.getSessionManager()
.getSessions(s -> s.get("e4ApplicationInstanceId").equals(currentInstanceId));
if (!sessions.isEmpty()) {
List<ISession> slaves = sessions.get(0).getSlaves();
for (ISession slave : slaves) {
slave.async(e -> {
slave.sendData(data);
return true;
}, null);
}
}
}
@Override
public void blockQueueTaking(boolean block) {
logger.debug("switch blocking {}", block);
blocking.set(block);
}
}