sensitivity plugins
This commit is contained in:
parent
240f9618f5
commit
5360129e5f
|
@ -47,8 +47,9 @@ import info.nightscout.androidaps.plugins.PumpDanaRKorean.services.DanaRKoreanEx
|
|||
import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Fragment;
|
||||
import info.nightscout.androidaps.plugins.PumpDanaRv2.services.DanaRv2ExecutionService;
|
||||
import info.nightscout.androidaps.plugins.PumpMDI.MDIFragment;
|
||||
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpFragment;
|
||||
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
|
||||
import info.nightscout.androidaps.plugins.SensitivityMK.SensitivityMKPlugin;
|
||||
import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin;
|
||||
import info.nightscout.androidaps.plugins.SmsCommunicator.SmsCommunicatorFragment;
|
||||
import info.nightscout.androidaps.plugins.SourceGlimp.SourceGlimpFragment;
|
||||
import info.nightscout.androidaps.plugins.SourceMM640g.SourceMM640gFragment;
|
||||
|
@ -98,6 +99,8 @@ public class MainApp extends Application {
|
|||
if (Config.ACTION) pluginsList.add(ActionsFragment.getPlugin());
|
||||
pluginsList.add(InsulinFastactingFragment.getPlugin());
|
||||
pluginsList.add(InsulinFastactingProlongedFragment.getPlugin());
|
||||
pluginsList.add(SensitivityOref0Plugin.getPlugin());
|
||||
pluginsList.add(SensitivityMKPlugin.getPlugin());
|
||||
if (Config.DANAR) pluginsList.add(DanaRFragment.getPlugin());
|
||||
if (Config.DANAR) pluginsList.add(DanaRKoreanFragment.getPlugin());
|
||||
if (Config.DANARv2) pluginsList.add(DanaRv2Fragment.getPlugin());
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.util.Date;
|
|||
public interface PluginBase {
|
||||
int GENERAL = 1;
|
||||
int TREATMENT = 2;
|
||||
//int TEMPBASAL = 3;
|
||||
int SENSITIVITY = 3;
|
||||
int PROFILE = 4;
|
||||
int APS = 5;
|
||||
int PUMP = 6;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.interfaces;
|
||||
|
||||
import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult;
|
||||
|
||||
/**
|
||||
* Created by mike on 24.06.2017.
|
||||
*/
|
||||
|
||||
public interface SensitivityInterface {
|
||||
AutosensResult detectSensitivity(long fromTime, long toTime);
|
||||
}
|
|
@ -34,9 +34,11 @@ import info.nightscout.androidaps.interfaces.InsulinInterface;
|
|||
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||
import info.nightscout.androidaps.interfaces.ProfileInterface;
|
||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface;
|
||||
import info.nightscout.androidaps.plugins.InsulinFastacting.InsulinFastactingPlugin;
|
||||
import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin;
|
||||
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
|
||||
import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin;
|
||||
import info.nightscout.utils.PasswordProtection;
|
||||
|
||||
|
||||
|
@ -49,6 +51,7 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
}
|
||||
|
||||
ListView insulinListView;
|
||||
ListView sensitivityListView;
|
||||
ListView bgsourceListView;
|
||||
TextView bgsourceLabel;
|
||||
ListView pumpListView;
|
||||
|
@ -71,6 +74,7 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
Button unlock;
|
||||
|
||||
PluginCustomAdapter insulinDataAdapter = null;
|
||||
PluginCustomAdapter sensivityDataAdapter = null;
|
||||
PluginCustomAdapter bgsourceDataAdapter = null;
|
||||
PluginCustomAdapter pumpDataAdapter = null;
|
||||
PluginCustomAdapter loopDataAdapter = null;
|
||||
|
@ -96,6 +100,7 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
smallWidth = screen_width < Constants.SMALL_WIDTH;
|
||||
|
||||
insulinListView = (ListView) view.findViewById(R.id.configbuilder_insulinlistview);
|
||||
sensitivityListView = (ListView) view.findViewById(R.id.configbuilder_sensitivitylistview);
|
||||
bgsourceListView = (ListView) view.findViewById(R.id.configbuilder_bgsourcelistview);
|
||||
bgsourceLabel = (TextView) view.findViewById(R.id.configbuilder_bgsourcelabel);
|
||||
pumpListView = (ListView) view.findViewById(R.id.configbuilder_pumplistview);
|
||||
|
@ -178,6 +183,9 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
setListViewHeightBasedOnChildren(apsListView);
|
||||
if (MainApp.getSpecificPluginsVisibleInList(PluginBase.APS).size() == 0)
|
||||
apsLabel.setVisibility(View.GONE);
|
||||
sensivityDataAdapter = new PluginCustomAdapter(getContext(), smallWidth?R.layout.configbuilder_smallitem :R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface.class, PluginBase.SENSITIVITY), PluginBase.SENSITIVITY);
|
||||
sensitivityListView.setAdapter(sensivityDataAdapter);
|
||||
setListViewHeightBasedOnChildren(sensitivityListView);
|
||||
constraintsDataAdapter = new PluginCustomAdapter(getContext(), smallWidth?R.layout.configbuilder_smallitem :R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface.class, PluginBase.BGSOURCE), PluginBase.CONSTRAINTS);
|
||||
constraintsListView.setAdapter(constraintsDataAdapter);
|
||||
setListViewHeightBasedOnChildren(constraintsListView);
|
||||
|
@ -277,7 +285,7 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
}
|
||||
|
||||
// Hide enabled control and force enabled plugin if there is only one plugin available
|
||||
if (type == PluginBase.INSULIN || type == PluginBase.PUMP || type == PluginBase.TREATMENT || type == PluginBase.PROFILE)
|
||||
if (type == PluginBase.INSULIN || type == PluginBase.PUMP || type == PluginBase.TREATMENT || type == PluginBase.PROFILE || type == PluginBase.SENSITIVITY)
|
||||
if (pluginList.size() < 2) {
|
||||
holder.checkboxEnabled.setEnabled(false);
|
||||
plugin.setFragmentEnabled(type, true);
|
||||
|
@ -326,6 +334,9 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
case PluginBase.INSULIN:
|
||||
pluginsInCategory = MainApp.getSpecificPluginsListByInterface(InsulinInterface.class);
|
||||
break;
|
||||
case PluginBase.SENSITIVITY:
|
||||
pluginsInCategory = MainApp.getSpecificPluginsListByInterface(SensitivityInterface.class);
|
||||
break;
|
||||
case PluginBase.APS:
|
||||
pluginsInCategory = MainApp.getSpecificPluginsListByInterface(APSInterface.class);
|
||||
break;
|
||||
|
@ -356,6 +367,8 @@ public class ConfigBuilderFragment extends Fragment {
|
|||
MainApp.getSpecificPlugin(VirtualPumpPlugin.class).setFragmentEnabled(type, true);
|
||||
else if (type == PluginBase.INSULIN)
|
||||
MainApp.getSpecificPlugin(InsulinFastactingPlugin.class).setFragmentEnabled(type, true);
|
||||
else if (type == PluginBase.SENSITIVITY)
|
||||
MainApp.getSpecificPlugin(SensitivityOref0Plugin.class).setFragmentEnabled(type, true);
|
||||
else if (type == PluginBase.PROFILE)
|
||||
MainApp.getSpecificPlugin(NSProfilePlugin.class).setFragmentEnabled(type, true);
|
||||
else
|
||||
|
|
|
@ -40,6 +40,7 @@ import info.nightscout.androidaps.interfaces.PluginBase;
|
|||
import info.nightscout.androidaps.interfaces.ProfileInterface;
|
||||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface;
|
||||
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
|
||||
import info.nightscout.androidaps.plugins.Loop.APSResult;
|
||||
import info.nightscout.androidaps.plugins.Loop.LoopPlugin;
|
||||
|
@ -65,6 +66,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain
|
|||
private static APSInterface activeAPS;
|
||||
private static LoopPlugin activeLoop;
|
||||
private static InsulinInterface activeInsulin;
|
||||
private static SensitivityInterface activeSensitivity;
|
||||
|
||||
static public String nightscoutVersionName = "";
|
||||
static public Integer nightscoutVersionCode = 0;
|
||||
|
@ -218,6 +220,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain
|
|||
log.debug(p.getName() + ":" +
|
||||
(p.isEnabled(1) ? " GENERAL" : "") +
|
||||
(p.isEnabled(2) ? " TREATMENT" : "") +
|
||||
(p.isEnabled(3) ? " SENSITIVITY" : "") +
|
||||
(p.isEnabled(4) ? " PROFILE" : "") +
|
||||
(p.isEnabled(5) ? " APS" : "") +
|
||||
(p.isEnabled(6) ? " PUMP" : "") +
|
||||
|
@ -256,6 +259,17 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain
|
|||
}
|
||||
}
|
||||
|
||||
// PluginBase.SENSITIVITY
|
||||
pluginsInCategory = MainApp.getSpecificPluginsListByInterface(SensitivityInterface.class);
|
||||
activeSensitivity = (SensitivityInterface) getTheOneEnabledInArray(pluginsInCategory, PluginBase.SENSITIVITY);
|
||||
if (Config.logConfigBuilder)
|
||||
log.debug("Selected sensitivity interface: " + ((PluginBase) activeSensitivity).getName());
|
||||
for (PluginBase p : pluginsInCategory) {
|
||||
if (!p.getName().equals(((PluginBase) activeSensitivity).getName())) {
|
||||
p.setFragmentVisible(PluginBase.SENSITIVITY, false);
|
||||
}
|
||||
}
|
||||
|
||||
// PluginBase.PROFILE
|
||||
pluginsInCategory = MainApp.getSpecificPluginsListByInterface(ProfileInterface.class);
|
||||
activeProfile = (ProfileInterface) getTheOneEnabledInArray(pluginsInCategory, PluginBase.PROFILE);
|
||||
|
@ -411,43 +425,6 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain
|
|||
return 0d;
|
||||
}
|
||||
|
||||
/*
|
||||
public PumpEnactResult deliverTreatmentFromBolusWizard(InsulinInterface insulinType, Context context, Double insulin, Integer carbs, Double glucose, String glucoseType, int carbTime, JSONObject boluscalc) {
|
||||
mWakeLock.acquire();
|
||||
PumpEnactResult result;
|
||||
insulin = applyBolusConstraints(insulin);
|
||||
carbs = applyCarbsConstraints(carbs);
|
||||
|
||||
BolusProgressDialog bolusProgressDialog = null;
|
||||
if (context != null) {
|
||||
bolusProgressDialog = new BolusProgressDialog();
|
||||
bolusProgressDialog.setInsulin(insulin);
|
||||
bolusProgressDialog.show(((AppCompatActivity) context).getSupportFragmentManager(), "BolusProgress");
|
||||
}
|
||||
|
||||
MainApp.bus().post(new EventBolusRequested(insulin));
|
||||
|
||||
result = activePump.deliverTreatment(insulinType, insulin, carbs, context);
|
||||
|
||||
BolusProgressDialog.bolusEnded = true;
|
||||
|
||||
MainApp.bus().post(new EventDismissBolusprogressIfRunning(result));
|
||||
|
||||
if (result.success) {
|
||||
Treatment t = new Treatment(insulinType);
|
||||
t.insulin = result.bolusDelivered;
|
||||
if (carbTime == 0)
|
||||
t.carbs = (double) result.carbsDelivered; // with different carbTime record will come back from nightscout
|
||||
t.date = System.currentTimeMillis();
|
||||
t.mealBolus = result.carbsDelivered > 0;
|
||||
addToHistoryTreatment(t);
|
||||
t.carbs = (double) result.carbsDelivered;
|
||||
NSUpload.uploadBolusWizardRecord(t, glucose, glucoseType, carbTime, boluscalc);
|
||||
}
|
||||
mWakeLock.release();
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) {
|
||||
mWakeLock.acquire();
|
||||
|
@ -473,56 +450,6 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context) {
|
||||
return deliverTreatment(insulinType, insulin, carbs, context, true);
|
||||
}
|
||||
|
||||
public PumpEnactResult deliverTreatment(InsulinInterface insulinType, Double insulin, Integer carbs, Context context, boolean createTreatment) {
|
||||
mWakeLock.acquire();
|
||||
PumpEnactResult result;
|
||||
insulin = applyBolusConstraints(insulin);
|
||||
carbs = applyCarbsConstraints(carbs);
|
||||
|
||||
BolusProgressDialog bolusProgressDialog = null;
|
||||
if (context != null) {
|
||||
bolusProgressDialog = new BolusProgressDialog();
|
||||
bolusProgressDialog.setInsulin(insulin);
|
||||
bolusProgressDialog.show(((AppCompatActivity) context).getSupportFragmentManager(), "BolusProgress");
|
||||
} else {
|
||||
Intent i = new Intent();
|
||||
i.putExtra("insulin", insulin.doubleValue());
|
||||
i.setClass(MainApp.instance(), BolusProgressHelperActivity.class);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
MainApp.instance().startActivity(i);
|
||||
}
|
||||
|
||||
MainApp.bus().post(new EventBolusRequested(insulin));
|
||||
|
||||
result = activePump.deliverTreatment(insulinType, insulin, carbs, context);
|
||||
|
||||
BolusProgressDialog.bolusEnded = true;
|
||||
|
||||
MainApp.bus().post(new EventDismissBolusprogressIfRunning(result));
|
||||
|
||||
if (Config.logCongigBuilderActions)
|
||||
log.debug("deliverTreatment insulin: " + insulin + " carbs: " + carbs + " success: " + result.success + " enacted: " + result.enacted + " bolusDelivered: " + result.bolusDelivered);
|
||||
|
||||
if (result.success && createTreatment) {
|
||||
Treatment t = new Treatment(insulinType);
|
||||
t.insulin = result.bolusDelivered;
|
||||
t.carbs = (double) result.carbsDelivered;
|
||||
t.date = System.currentTimeMillis();
|
||||
t.mealBolus = t.carbs > 0;
|
||||
addToHistoryTreatment(t);
|
||||
NSUpload.uploadTreatment(t);
|
||||
}
|
||||
mWakeLock.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
*/
|
||||
@Override
|
||||
public void stopBolusDelivering() {
|
||||
activePump.stopBolusDelivering();
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package info.nightscout.androidaps.plugins.SensitivityMK;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface;
|
||||
import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult;
|
||||
|
||||
/**
|
||||
* Created by mike on 24.06.2017.
|
||||
*/
|
||||
|
||||
public class SensitivityMKPlugin implements PluginBase, SensitivityInterface{
|
||||
private static boolean fragmentEnabled = true;
|
||||
private static boolean fragmentVisible = false;
|
||||
|
||||
static SensitivityMKPlugin plugin = null;
|
||||
|
||||
public static SensitivityMKPlugin getPlugin() {
|
||||
if (plugin == null)
|
||||
plugin = new SensitivityMKPlugin();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return INSULIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return MainApp.sResources.getString(R.string.sensitivitymk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameShort() {
|
||||
return MainApp.sResources.getString(R.string.sensitivity_shortname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int type) {
|
||||
return type == SENSITIVITY && fragmentEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisibleInTabs(int type) {
|
||||
return type == SENSITIVITY && fragmentVisible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeHidden(int type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFragment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showInList(int type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFragmentEnabled(int type, boolean fragmentEnabled) {
|
||||
if (type == SENSITIVITY) this.fragmentEnabled = fragmentEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFragmentVisible(int type, boolean fragmentVisible) {
|
||||
if (type == SENSITIVITY) this.fragmentVisible = fragmentVisible;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AutosensResult detectSensitivity(long fromTime, long toTime) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package info.nightscout.androidaps.plugins.SensitivityOref0;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface;
|
||||
import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult;
|
||||
|
||||
/**
|
||||
* Created by mike on 24.06.2017.
|
||||
*/
|
||||
|
||||
public class SensitivityOref0Plugin implements PluginBase, SensitivityInterface{
|
||||
private static boolean fragmentEnabled = true;
|
||||
private static boolean fragmentVisible = false;
|
||||
|
||||
static SensitivityOref0Plugin plugin = null;
|
||||
|
||||
public static SensitivityOref0Plugin getPlugin() {
|
||||
if (plugin == null)
|
||||
plugin = new SensitivityOref0Plugin();
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return INSULIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return MainApp.sResources.getString(R.string.sensitivityoref0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameShort() {
|
||||
return MainApp.sResources.getString(R.string.sensitivity_shortname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int type) {
|
||||
return type == SENSITIVITY && fragmentEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisibleInTabs(int type) {
|
||||
return type == SENSITIVITY && fragmentVisible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeHidden(int type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFragment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showInList(int type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFragmentEnabled(int type, boolean fragmentEnabled) {
|
||||
if (type == SENSITIVITY) this.fragmentEnabled = fragmentEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFragmentVisible(int type, boolean fragmentVisible) {
|
||||
if (type == SENSITIVITY) this.fragmentVisible = fragmentVisible;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AutosensResult detectSensitivity(long fromTime, long toTime) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -41,7 +41,6 @@
|
|||
android:layout_marginRight="10dp"
|
||||
android:background="@color/cardColorBackground" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/configbuilder_bgsourcelabel"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -77,6 +76,21 @@
|
|||
android:layout_marginRight="10dp"
|
||||
android:background="@color/cardColorBackground" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:text="@string/configbuilder_sensitivity"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/configbuilder_sensitivitylistview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:background="@color/cardColorBackground" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/configbuilder_apslabel"
|
||||
|
|
|
@ -653,4 +653,8 @@
|
|||
<string name="key_do_not_track_profile_switch">do_not_track_profile_switch</string>
|
||||
<string name="do_not_track_profile_switch">Ignore profile switch events</string>
|
||||
<string name="do_not_track_profile_switch_summary">All profile switch events are ignoreg and active profile is always used</string>
|
||||
<string name="configbuilder_sensitivity">Sensitivity detection</string>
|
||||
<string name="sensitivity_shortname">SENS</string>
|
||||
<string name="sensitivityoref0">Sensitivity Oref0</string>
|
||||
<string name="sensitivitymk">Sensitivity MK</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue