blob: 06a3e5c3a574e8f7a92ff4cebd963ba1e775263f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2009 Wind River Systems and others.
*
* 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
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.examples.dsf.timers;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin;
import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext;
import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerTickDMEvent;
import org.osgi.framework.BundleContext;
/**
* The alarm service tracks triggers and alarms. Triggers have a specified
* value and can be created and removed independently. Alarms are created
* for a specific timer and a trigger, and can indicate whether an alarm is
* triggered.
* <p>
* This service depends on the TimerService, so the TimerService has to be
* initialized before this service is initialized.
* </p>
*/
public class AlarmService extends AbstractDsfService {
/** Event indicating that the list of triggers is changed. */
@Immutable
public static class TriggersChangedEvent {
}
/** Context representing an alarm tracked by this service. */
@Immutable
public static class TriggerDMContext extends AbstractDMContext {
/** Alarm number, also index into alarm map */
final int fNumber;
private TriggerDMContext(String sessionId, int number) {
super(sessionId, new IDMContext[0]);
fNumber = number;
}
@Override
public boolean equals(Object other) {
return baseEquals(other) && ((TriggerDMContext) other).fNumber == fNumber;
}
public int getTriggerNumber() {
return fNumber;
}
@Override
public int hashCode() {
return baseHashCode() + fNumber;
}
@Override
public String toString() {
return baseToString() + ".trigger[" + fNumber + "]";
}
}
/**
* Context representing the "triggered" status of an alarm with respect to
* a specific timer.
*/
@Immutable
public static class AlarmDMContext extends AbstractDMContext {
// An alarm requires both a timer and alarm context, both of which
// become parents of the alarm context.
// Note: beyond the parent contexts this context does not contain
// any other data, because no other data is needed.
private AlarmDMContext(String sessionId, TimerDMContext timerCtx, TriggerDMContext alarmCtx) {
super(sessionId, new IDMContext[] { timerCtx, alarmCtx });
}
@Override
public boolean equals(Object other) {
return baseEquals(other);
}
@Override
public int hashCode() {
return baseHashCode();
}
@Override
public String toString() {
return baseToString() + ":alarm"; //$NON-NLS-1$
}
}
/**
* Event indicating that an alarm has been triggered by a timer.
*/
public class AlarmTriggeredDMEvent extends AbstractDMEvent<AlarmDMContext> {
public AlarmTriggeredDMEvent(AlarmDMContext context) {
super(context);
}
}
private int fTriggerNumberCounter = 1;
private Map<TriggerDMContext, Integer> fTriggers = new LinkedHashMap<>();
AlarmService(DsfSession session) {
super(session);
}
@Override
protected BundleContext getBundleContext() {
return DsfExamplesPlugin.getDefault().getBundle().getBundleContext();
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleSuccess() {
// After super-class is finished initializing
// perform TimerService initialization.
doInitialize(requestMonitor);
}
});
}
private void doInitialize(RequestMonitor requestMonitor) {
// Add this class as a listener for service events, in order to receive
// TimerTickEvent events.
getSession().addServiceEventListener(this, null);
// Register service
register(new String[] { AlarmService.class.getName() }, new Hashtable<String, String>());
requestMonitor.done();
}
@Override
public void shutdown(RequestMonitor requestMonitor) {
getSession().removeServiceEventListener(this);
unregister();
super.shutdown(requestMonitor);
}
public boolean isValid() {
return true;
}
@DsfServiceEventHandler
public void eventDispatched(TimerTickDMEvent event) {
final TimerDMContext timerContext = event.getDMContext();
int timerValue = getServicesTracker().getService(TimerService.class).getTimerValue(event.getDMContext());
// If a timer triggers an alarm, this service needs to issue an alarm
// triggered event.
checkAlarmsForTimer(timerContext, timerValue);
}
private void checkAlarmsForTimer(TimerDMContext timerContext, int timerValue) {
// Check the existing alarms for whether they are triggered by given
// timer.
for (Map.Entry<TriggerDMContext, Integer> entry : fTriggers.entrySet()) {
if (timerValue == entry.getValue()) {
// Generate the AlarmTriggeredEvent
AlarmDMContext alarmCtx = new AlarmDMContext(getSession().getId(), timerContext, entry.getKey());
getSession().dispatchEvent(new AlarmTriggeredDMEvent(alarmCtx), getProperties());
}
}
}
/** Returns the list of triggers. */
public TriggerDMContext[] getTriggers() {
return fTriggers.keySet().toArray(new TriggerDMContext[fTriggers.size()]);
}
/** Returns the trigger value. */
public int getTriggerValue(TriggerDMContext alarmCtx) {
Integer value = fTriggers.get(alarmCtx);
if (value != null) {
return value;
} else {
return -1;
}
}
/** Returns the alarm context for given timer and trigger contexts. */
public AlarmDMContext getAlarm(TriggerDMContext alarmCtx, TimerDMContext timerCtx) {
return new AlarmDMContext(getSession().getId(), timerCtx, alarmCtx);
}
/** Returns true if the given alarm is triggered */
public boolean isAlarmTriggered(AlarmDMContext alarmCtx) {
// Extract the timer and trigger contexts. They should always be part
// of the alarm.
TimerService.TimerDMContext timerCtx = DMContexts.getAncestorOfType(alarmCtx,
TimerService.TimerDMContext.class);
TriggerDMContext triggerCtx = DMContexts.getAncestorOfType(alarmCtx, TriggerDMContext.class);
assert triggerCtx != null && timerCtx != null;
// Find the trigger and check whether the timers value has surpassed it.
if (fTriggers.containsKey(triggerCtx)) {
int timerValue = getServicesTracker().getService(TimerService.class).getTimerValue(timerCtx);
return timerValue >= fTriggers.get(triggerCtx);
}
return false;
}
/** Creates a new alarm object with given value. */
public TriggerDMContext createTrigger(int value) {
TriggerDMContext triggerCtx = new TriggerDMContext(getSession().getId(), fTriggerNumberCounter++);
fTriggers.put(triggerCtx, value);
getSession().dispatchEvent(new TriggersChangedEvent(), getProperties());
return triggerCtx;
}
/** Removes given alarm from service. */
public void deleteTrigger(TriggerDMContext alarmCtx) {
fTriggers.remove(alarmCtx);
getSession().dispatchEvent(new TriggersChangedEvent(), getProperties());
}
/** Changes the value of the given trigger. */
public void setTriggerValue(TriggerDMContext ctx, int newValue) {
if (fTriggers.containsKey(ctx)) {
fTriggers.put(ctx, newValue);
}
getSession().dispatchEvent(new TriggersChangedEvent(), getProperties());
}
}